home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-02
/
fgl110b.zip
/
USER2.TXT
< prev
Wrap
Text File
|
1992-02-07
|
323KB
|
7,674 lines
Chapter 9
Images and
Image Management
136 Fastgraph User's Guide
Overview
Within the context of Fastgraph, an image is a rectangular area of video
memory containing some type of picture. An image might be something as
simple as a pointing hand icon, or as detailed as the dashboard of a sports
car. Fastgraph includes several routines to display, retrieve, and
manipulate images, as well as transfer them between different areas of video
memory. This chapter will discuss these routines in detail. The information
presented here, combined with the video page management techniques described
in the previous chapter, will provide the tools we need for sophisticated
animation techniques.
Mode-Independent Bit-Mapped Images
This section will discuss the image display routines that use the same
bit-mapped image format for all graphics video modes. Another class of
routines, described in the next section, use different formats for different
video modes. While these mode-independent image display routines are more
general, they achieve this generality at the sake of execution speed. This
may especially be a concern if the image is large, or if speed is critical in
an application (as in arcade-style graphics). For many programs, however,
the mode-independent routines provide all the image display capability
needed.
Let's begin with an example of a very simple image. Suppose we need to
display a small triangle whose perimeter is a different color than its
interior. To use this image with Fastgraph, we must inscribe it in a
rectangular area. Hence, the pixel representation of our triangle might
appear as shown below.
. . . . * . . . .
. . . * x * . . .
. . * x x x * . .
. * x x x x x * .
* * * * * * * * *
As shown in this diagram, our triangle is 9 pixels wide at its base and
5 pixels high. The pixels indicated by an asterisk (*) are the triangle's
perimeter, while those indicated by an x represent its interior points. We
need to distinguish between these pixels because they will be different
colors. The pixels shown as periods (.) are not part of the triangle itself.
They are required to make the image rectangular, so from Fastgraph's
perspective they are indeed part of the image.
The Fastgraph routine fg_drawmap is a suitable routine for drawing our
triangle. To use fg_drawmap, we must create separate bit maps for each color
in the image (excluding the points used to fill the rectangular region, which
is considered transparent). In this example, we will thus need two bit
maps -- one for the perimeter points, and one for the interior points. Let's
break the image into these two bit maps.
Chapter 9: Images and Image Management 137
. . . . * . . . . . . . . . . . . .
. . . * . * . . . . . . . x . . . .
. . * . . . * . . . . . x x x . . .
. * . . . . . * . . . x x x x x . .
* * * * * * * * * . . . . . . . . .
perimeter points interior points
The next step is to convert these two bit maps into their binary
representations. Just as there are eight bits in a byte, we will create a
data structure (an array in this case) with each byte holding eight pixels.
Bits that are set (1) indicate the corresponding pixel will appear displayed
in the color associated with that bit map. Bits that are reset (0) leave the
corresponding pixel unchanged. The size of each bit map array must be at
least 10 bytes because each bit map contains five rows with nine pixels in
each row (that is, two bytes are required for each row of the image). Hence,
when we convert these bit maps to their binary representations, and
subsequently to their hexadecimal equivalent, the results will appear as
shown below. The boldface bits represent the actual image; the other bits
are filler bits needed to complete each row of the bit maps after the ninth
pixel. All filler bits must be zero.
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 08 00
0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 14 00
0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 22 00
0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 41 00
1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 FF 80
perimeter bit map
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 00
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 08 00
0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1C 00
0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 3E 00
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 00
interior bit map
The next question is the order in which the bit maps are stored in the
corresponding data structures. Since our data structure is an array, it is
only necessary to show the relationship of the subscripts to the bit map
structures above. The next diagram shows the subscript order for the case of
a two-column by five-row bit map.
[8] [9]
[6] [7]
[4] [5]
[2] [3]
[0] [1]
138 Fastgraph User's Guide
From this diagram, we see the first element of the array (that is, the
element with subscript [0]) represents the lower left corner of the image.
The subscript progression then continues right until reaching the end of the
first row. It then resumes at the leftmost element of the second row and
continues to the right until the end of that row. It continues in this
manner for all remaining rows.
We are now ready to present an example program to display our triangle.
The program will use the Fastgraph routine fg_drawmap, which expects three
arguments. The first argument is the bit map array (passed by reference),
the second is the width of the bit map in bytes, and the last is the height
of the bit map in pixel rows. The fg_drawmap routine displays the image such
that its lower left corner is at the graphics cursor position on the active
video page. The routine has no effect in text video modes. Additionally,
fg_drawmap displays the image using the current color index, which means we
will need to call fg_drawmap once for each color in the image.
Example 9-1 runs in any 320 by 200 color graphics mode (it could be made
to run in mode 12 too, but that would detract from the purpose of the
example). After establishing the video mode, the program uses fg_rect to
fill the entire screen with a gray rectangle (white in CGA). Next, the
program establishes (156,101) as the graphics cursor position; this causes
the triangle to be centered on the screen. The two calls to fg_drawmap, one
for each of the colors in the image, actually display the triangle. Note
especially how fg_setcolor is used before each call to fg_drawmap to define
the current color index. The result is a triangle with a blue perimeter
(cyan in CGA) and green interior (magenta in CGA).
Example 9-1.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
char perimeter[] = {
0xFF,0x80,0x41,0x00,0x22,0x00,0x14,0x00,0x08,0x00
};
char interior[] = {
0x00,0x00,0x3E,0x00,0x1C,0x00,0x08,0x00,0x00,0x00
};
void main()
{
int old_mode, new_mode;
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
Chapter 9: Images and Image Management 139
fg_setcolor(7);
fg_rect(0,319,0,199);
fg_move(156,101);
fg_setcolor(1);
fg_drawmap(perimeter,2,5);
fg_setcolor(2);
fg_drawmap(interior,2,5);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
The different color bit maps used by fg_drawmap do not all have to be
the same size. In our triangle example, the perimeter is 9 pixels wide by 5
pixels high, but the interior is only 5 pixels wide by 3 pixels high. Hence,
the bit map for the interior pixels only requires one byte for each of its
three rows, so we can store it in a three-byte array. Its structure would
be:
[2] 08
[1] 1C
[0] 3E
Example 9-2 is similar to example 9-1, but it uses a three-byte array
for the interior bit map. Note the second call to fg_move in this example.
It is needed because the bottom row of the smaller interior bit map
corresponds to the second row of the larger perimeter bit map. In other
words, the interior bit map must be displayed one row above the perimeter bit
map.
Example 9-2.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
char perimeter[] = {
0xFF,0x80,0x41,0x00,0x22,0x00,0x14,0x00,0x08,0x00
};
char interior[] = {
0x3E,0x1C,0x08
};
void main()
{
int old_mode, new_mode;
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
140 Fastgraph User's Guide
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_setcolor(7);
fg_rect(0,319,0,199);
fg_move(156,101);
fg_setcolor(1);
fg_drawmap(perimeter,2,5);
fg_move(156,100);
fg_setcolor(2);
fg_drawmap(interior,1,3);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
In example 9-2, the time required to execute the second call to fg_move
may not be worth the saving of 7 bytes. When array space is critical, or
when the images are larger, the use of smaller bit maps for certain colors
may be more valuable.
Yet another possibility for example 9-2 would be to shift the elements
of the interior bit map two pixels to the left. In this way, the bit map
would be aligned against the left side of the array, just as the perimeter
bit map is. The three values comprising the interior bit map would then
become F8, 70, and 20. We also would need to change the x coordinate in the
second call to fg_move from 156 to 158.
Mode-Specific Bit-Mapped Images
This section will discuss the image display routines that use bit-mapped
image formats that are specific to each text and graphics video mode. The
different image formats closely resemble the structure of video memory in
each mode, so these routines are much faster than displaying mode-independent
bit maps with fg_drawmap. If you use the mode-specific bit maps in a program
that supports several video modes, there will be some additional programming
that is not needed when using mode-independent bit maps. Usually, however,
your efforts will be rewarded with faster graphics.
We'll demonstrate the use of mode-specific bit maps in graphics modes
with the familiar two-color triangle whose pixel representation appears
below.
. . . . * . . . .
. . . * x * . . .
. . * x x x * . .
. * x x x x x * .
* * * * * * * * *
Chapter 9: Images and Image Management 141
As before, our triangle is 9 pixels wide at its base and 5 pixels high.
The pixels indicated by an asterisk (*) are the triangle's perimeter, while
those indicated by an x represent its interior points. We need to
distinguish between these pixels because they will be different colors. The
pixels shown as periods (.) are not part of the triangle itself. They are
required to make the image rectangular, so from Fastgraph's perspective they
are indeed part of the image.
Regular Images
The Fastgraph routine fg_drwimage displays regular mode-specific bit-
mapped images (by regular, we mean an image that is neither clipped nor
rotated). Its arguments are the same as for the fg_drawmap routine, and the
bit map array's subscript order is also the same as for fg_drawmap. The
major difference is the bit map structure -- we combine the information for
all colors into a single bit map, in a way consistent with the structure and
accessibility of video memory for the various modes. As with the other image
display routines, fg_drwimage displays the image on the active video page
with its lower left corner at the graphics cursor position (or the text
cursor position for text modes). We'll now examine the use of fg_drwimage in
several video modes.
CGA four-color graphics modes
In the four-color CGA graphics modes (modes 4 and 5), each pixel can
assume a value between 0 and 3. This means it takes two bits to represent a
pixel, or put another way, each byte of video memory holds four pixels. Our
triangle image is nine pixels wide, so three bytes are needed for each row of
the image. Because the image is five pixels high, we need a bit map array of
at least 15 bytes (five rows times three bytes per row) to hold the image.
The image's binary representation and its hexadecimal equivalent for the
four-color CGA graphics modes are shown below. The binary values in boldface
represent the actual image; the others are the filler bits needed to complete
each row of the bit map after the ninth pixel. We have coded the perimeter
pixels to be color 1 (01 binary) and the interior pixels to be color 2 (10
binary). Any pixel whose value is zero (00 binary) is transparent and will
thus leave the contents of video memory at that position unchanged.
00 00 00 00 01 00 00 00 00 00 00 00 00 40 00
00 00 00 01 10 01 00 00 00 00 00 00 01 90 00
00 00 01 10 10 10 01 00 00 00 00 00 06 A4 00
00 01 10 10 10 10 10 01 00 00 00 00 1A A9 00
01 01 01 01 01 01 01 01 01 00 00 00 55 55 40
Example 9-3 uses this mode-specific bit map to display the triangle in
the standard CGA four-color graphics mode (mode 4). After establishing the
video mode, the program uses fg_rect to fill the entire screen with a white
rectangle. Next, the program establishes (156,101) as the graphics cursor
position; this causes the triangle to be centered on the screen. The call to
142 Fastgraph User's Guide
fg_drwimage produces a triangle with a cyan perimeter (color 1) and a magenta
interior (color 2).
Example 9-3.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
char triangle[] = {
0x55,0x55,0x40, 0x1A,0xA9,0x00, 0x06,0xA4,0x00,
0x01,0x90,0x00, 0x00,0x40,0x00
};
void main()
{
int old_mode;
if (fg_testmode(4,1) == 0) {
printf("This program requires a 320 ");
printf("x 200 CGA graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(4);
fg_setcolor(7);
fg_rect(0,319,0,199);
fg_move(156,101);
fg_drwimage(triangle,3,5);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
CGA two-color graphics mode
In the two-color CGA graphics mode (mode 6), each pixel can assume the
values 0 or 1. This means it takes just one bit to represent a pixel, so
each byte of video memory holds eight pixels. Our triangle image is nine
pixels wide, so two bytes are needed for each row of the image. Because the
image is five pixels high, we need a bit map array of at least 10 bytes (five
rows times two bytes per row) to hold the image.
The image's binary representation and its hexadecimal equivalent for the
two-color CGA graphics mode is shown below. The binary values in boldface
represent the actual image; the others are the filler bits needed to complete
each row of the bit map after the ninth pixel. We have coded both the
perimeter pixels and the interior pixels to be color 1. Any pixel whose
value is zero is transparent and will thus leave the contents of video memory
at that position unchanged.
Chapter 9: Images and Image Management 143
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 08 00
0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1C 00
0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 3E 00
0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 7F 00
1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 FF 80
Example 9-4 uses this mode-specific bit map to display the triangle in
the CGA two-color graphics mode (mode 6). After establishing the video mode,
the program establishes (316,101) as the graphics cursor position; this
causes the triangle to be centered on the screen. The call to fg_drwimage
produces a solid triangle.
Example 9-4.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
char triangle[] = {
0xFF,0x80, 0x7F,0x00, 0x3E,0x00,
0x1C,0x00, 0x08,0x00
};
void main()
{
int old_mode;
if (fg_testmode(6,1) == 0) {
printf("This program requires a ");
printf("CGA graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(6);
fg_move(316,101);
fg_drwimage(triangle,2,5);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Tandy/PCjr 16-color graphics mode
The structure of the mode-specific bit maps for the Tandy/PCjr 16-color
graphics mode (mode 9) is the same as the mode-specific bit map structure for
the native EGA and VGA graphics modes. Please refer to page 144 for a
discussion of EGA and VGA bit maps.
144 Fastgraph User's Guide
Hercules graphics modes
The structure of the mode-specific bit maps for the Hercules graphics
modes (modes 11 and 12) is the same as two of the CGA graphics modes. For
the standard Hercules graphics mode (mode 11), please refer to the discussion
of CGA two-color (mode 6) bit maps on page 142. For the low-resolution
Hercules graphics mode (mode 12), please refer to the discussion of the CGA
four-color (mode 4) bit maps on page 141.
EGA and VGA graphics modes
In the native EGA and VGA graphics modes (modes 13 through 18), each
pixel can assume a value between 0 and 15. This means it takes four bits to
represent a pixel, so each byte of the bit map holds two pixels. Our
triangle image is nine pixels wide, so five bytes are needed for each row of
the image. Because the image is five pixels high, we need a bit map array of
at least 25 bytes (five rows times five bytes per row) to hold the image.
In these modes, it is easy to develop the hexadecimal representation of
a bit map without first producing its binary equivalent. This is because a
pixel value and a hexadecimal digit each occupy four bits. The triangle's
hexadecimal representation for the native EGA and VGA modes is shown below.
The pixels in boldface represent the actual image; the others are the filler
values needed to complete each row of the bit map after the ninth pixel. We
have chosen to display the perimeter pixels in color 1 and the interior
pixels in color 2. Any pixel whose value is zero is transparent and will
thus leave the contents of video memory at that position unchanged.
00 00 10 00 00
00 01 21 00 00
00 12 22 10 00
01 22 22 21 00
11 11 11 11 10
Example 9-5 is similar to example 9-3, but it uses the 320 x 200 EGA
graphics mode (mode 13) and the mode-specific bit map just constructed to
display the triangle. The call to fg_drwimage produces a triangle with a
blue perimeter (color 1) and a green interior (color 2).
Example 9-5.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
char triangle[] = {
0x11,0x11,0x11,0x11,0x10,
0x01,0x22,0x22,0x21,0x00,
0x00,0x12,0x22,0x10,0x00,
0x00,0x01,0x21,0x00,0x00,
0x00,0x00,0x10,0x00,0x00
};
Chapter 9: Images and Image Management 145
void main()
{
int old_mode;
if (fg_testmode(13,1) == 0) {
printf("This program requires a 320 ");
printf("x 200 EGA graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(13);
fg_setcolor(7);
fg_rect(0,319,0,199);
fg_move(156,101);
fg_drwimage(triangle,5,5);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
MCGA and VGA 256-color graphics modes
In the MCGA and VGA 256-color graphics modes (modes 19 through 23), each
pixel can assume a value between 0 and 255 (FF hex). This means it takes
eight bits to represent a pixel, or each byte of video memory holds one
pixel. Our triangle image is nine pixels wide, so nine bytes are needed for
each row of the image. Because the image is five pixels high, we need a bit
map array of at least 45 bytes (five rows times nine bytes per row) to hold
the image. Note we will never need any filler bits in the 256-color video
modes.
In the 256-color graphics video modes, it is simple to develop the bit
map for an image because each byte holds exactly one pixel. The triangle's
hexadecimal representation for the 256-color graphics modes is shown below.
As before, we have coded the perimeter pixels to be color 1 (01 hex) and the
interior pixels to be color 2 (02 hex). Any pixel whose value is zero is
transparent and will thus leave the contents of video memory at that position
unchanged.
00 00 00 00 01 00 00 00 00
00 00 00 01 02 01 00 00 00
00 00 01 02 02 02 01 00 00
00 01 02 02 02 02 02 01 00
01 01 01 01 01 01 01 01 01
146 Fastgraph User's Guide
Example 9-6 is also similar to example 9-3, but it uses the MCGA 256-
color graphics mode (mode 19) and the mode-specific bit map just constructed
to display the triangle. The call to fg_drwimage produces a triangle with a
blue perimeter (color 1) and a green interior (color 2).
Example 9-6.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
char triangle[] = {
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x00,0x01,0x02,0x02,0x02,0x02,0x02,0x01,0x00,
0x00,0x00,0x01,0x02,0x02,0x02,0x01,0x00,0x00,
0x00,0x00,0x00,0x01,0x02,0x01,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00
};
void main()
{
int old_mode;
if (fg_testmode(19,1) == 0) {
printf("This program requires a 320 ");
printf("x 200 MCGA graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(19);
fg_setcolor(7);
fg_rect(0,319,0,199);
fg_move(156,101);
fg_drwimage(triangle,9,5);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Text Modes
You also can use the fg_drwimage routine to display images in text video
modes (modes 0, 1, 2, 3, and 7). As one might expect, the image structure in
the text modes is rather different from the graphics modes. In Chapter 5 we
saw that each character cell on the screen actually consists of a character
and an attribute. The character value determines what character is
displayed, while the attribute value controls the character's appearance.
The structure of the attribute is:
Chapter 9: Images and Image Management 147
bits attribute
0-3 foreground color
4-6 background color
7 blinking
The text mode image structure used with fg_drwimage also consists of a
series of characters and attributes. For example, the following diagram
illustrates the structure of an image that is three characters wide and two
characters high.
char attr char attr char attr
char attr char attr char attr
To illustrate the use of fg_drwimage in a text video mode, we'll display
the phrase "hello there" on two different lines in the center of the screen.
Furthermore, let's assume we would like the first character of each word to
appear in foreground color 1, the second in color 2, and so forth. Our image
will consist of two lines each containing five characters, and each character
requires two bytes of storage (one for the character and another for its
attribute), so we'll need a 20-byte array for holding the image. The array
really doesn't hold a bit map as in the graphics modes, so in the text modes
the first argument passed to fg_drwimage is instead called the image array.
In our example, the structure of the image array is:
'h' 1 'e' 2 'l' 3 'l' 4 'o' 5
't' 1 'h' 2 'e' 3 'r' 4 'e' 5
The subscript order that fg_drwimage uses for text modes is the same as for
the graphics modes. For our five-row by two-column image, this means the
array subscripts would be numbered as follows:
[10] [11] [12] [13] [14] [15] [16] [17] [18] [19]
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
Depending on the character and attribute values in the image array,
fg_drwimage can display new characters and attributes, new characters leaving
the existing attribute unchanged, new attributes leaving the existing
character unchanged, or leave both the existing character and attribute
unchanged in video memory. To keep an existing character or attribute,
simply specify a value of 0 in the corresponding element of the image array.
This capability is analogous to the fact that zero-valued pixels in graphics
mode bit maps leave video memory unchanged.
148 Fastgraph User's Guide
Example 9-7 demonstrates the use of the fg_drwimage routine in the 80-
column color text mode (mode 3). After establishing the video mode and
making the cursor invisible, the program calls fg_drwimage to display the
"hello there" image just discussed (note we pass the dimensions of the image
array as the number of bytes, not the number of characters). The program
waits for a keystroke and then calls fg_drwimage again, passing a different
image array (called "image") of the same size. This array changes the first
letter of both words from lower case to upper case (leaving the attribute
unchanged), and it makes the remaining characters have the same attribute as
the first character. This is done in part by using zero-valued characters
and attributes to leave video memory unchanged. After waiting for another
keystroke, the program exits.
Example 9-7.
#include <fastgraf.h>
void main(void);
char hello[] = {
't',1, 'h',2, 'e',3, 'r',4, 'e',5,
'h',1, 'e',2, 'l',3, 'l',4, 'o',5
};
char image[] = {
'T',0, 0,1, 0,1, 0,1, 0,1,
'H',0, 0,1, 0,1, 0,1, 0,1
};
void main()
{
int old_mode;
old_mode = fg_getmode();
fg_setmode(3);
fg_cursor(0);
fg_locate(12,37);
fg_drwimage(hello,10,2);
fg_waitkey();
fg_drwimage(image,10,2);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Clipped Images
The fg_drwimage routine displays an image without regard to the current
clipping limits. If you want the image to be displayed with respect to the
clipping limits (as established by the most recent call to fg_setclip), you
should use the fg_clpimage routine instead of fg_drwimage. Fg_clpimage takes
the same three arguments as fg_drwimage, and also displays the image such
that its lower left corner is at the graphics cursor position. Unlike
fg_drwimage, the fg_clpimage routine has no effect when used in a text video
Chapter 9: Images and Image Management 149
mode. Refer to pages 149 and 151 for example programs that use fg_clpimage.
Because of the additional overhead involved in checking the clipping limits,
fg_clpimage is not as fast as fg_drwimage.
Reversed Images
The fg_revimage routine displays an image reversed (that is, mirrored
about the y-axis). Fg_revimage takes the same three arguments as
fg_drwimage, and also displays the image such that its lower left corner is
at the graphics cursor position. The fg_revimage routine has no effect when
used in a text video mode. Refer to pages 150 and 151 for example programs
that use fg_revimage.
Reversed Clipped Images
The fg_flpimage routine combines the effects of the fg_revimage and
fg_clpimage routines -- it displays a reversed image with respect to the
current clipping limits. Fg_flpimage takes the same three arguments as
fg_drwimage, and also displays the image such that its lower left corner is
at the graphics cursor position. Like the fg_clpimage routine, fg_flpimage
has no effect when used in a text video mode. Refer to pages 149 and 151 for
example programs that use fg_flpimage.
Some Examples
Example 9-8 illustrates the use of the fg_drwimage, fg_clpimage,
fg_revimage, and fg_flpimage routines in the standard CGA four-color graphics
mode (mode 4). The program uses each of these routines to display a small
white arrow, as shown in the pixel map below.
. . . . . . * . . .
. . . . . . * * . .
* * * * * * * * * .
* * * * * * * * * *
* * * * * * * * * .
. . . . . . * * . .
. . . . . . * . . .
As before, we must first convert this image to a bit map. The image is ten
pixels wide and seven high. In mode 4, each pixel occupies two bits, so we
need a 21-byte array (7 rows by 3 columns) to store the image. Since we want
to make the arrow white, each pixel will be displayed in color 3 (11 binary).
Here is the bit map and its hexadecimal equivalent for the arrow image in
mode 4 (the actual image is in boldface).
00 00 00 00 00 00 11 00 00 00 00 00 00 0C 00
00 00 00 00 00 00 11 11 00 00 00 00 00 0F 00
11 11 11 11 11 11 11 11 11 00 00 00 FF FF C0
11 11 11 11 11 11 11 11 11 11 00 00 FF FF F0
11 11 11 11 11 11 11 11 11 00 00 00 FF FF C0
00 00 00 00 00 00 11 11 00 00 00 00 00 0F 00
00 00 00 00 00 00 11 00 00 00 00 00 00 0C 00
150 Fastgraph User's Guide
After establishing the video mode, the program defines the clipping
region. It then uses fg_drwimage to display the arrow pointing to the right
and fg_clpimage to do the same thing, but with respect to the clipping
limits. Because the left edge of the arrow is displayed at x=10 and the
right clipping limit is at x=15, the call to fg_clpimage only draws the first
six columns of the arrow (that is, it does not draw the arrow head).
Next, example 9-8 uses fg_revimage to display the arrow pointing to the
left. To allow for the filler pixels, we must establish the graphics cursor
position two pixels to the left of the position used by fg_drwimage if we
want the tip of the left-pointing arrow to align with the tail of the right-
pointing arrow. Finally, the program uses fg_flpimage to display an arrow
pointing to the left with regard to the clipping limits. The call to
fg_flpimage displays the arrow head and the first two columns of the arrow
shaft.
Example 9-8.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
char arrow[] = {
0x00,0x0C,0x00, 0x00,0x0F,0x00, 0xFF,0xFF,0xC0,
0xFF,0xFF,0xF0, 0xFF,0xFF,0xC0, 0x00,0x0F,0x00,
0x00,0x0C,0x00
};
void main()
{
int old_mode;
if (fg_testmode(4,1) == 0) {
printf("This program requires a 320 ");
printf("x 200 CGA graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(4);
fg_setclip(0,15,0,199);
fg_move(10,10);
fg_drwimage(arrow,3,7);
fg_move(10,20);
fg_clpimage(arrow,3,7);
fg_move(8,30);
fg_revimage(arrow,3,7);
fg_move(8,40);
fg_flpimage(arrow,3,7);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Chapter 9: Images and Image Management 151
Example 9-9 is the same as example 9-8, but it uses the low resolution
EGA graphics mode (mode 13). If we changed the mode number specified in the
calls to fg_testmode and fg_setmode, the program also would run in any native
EGA or VGA graphics mode, or in the Tandy/PCjr 16-color graphics mode. In
these modes, we store two pixels per byte in the bit map array, so we need a
35-byte array (7 rows by 5 columns) to store the image. Here is the bit
map's hexadecimal equivalent for the arrow image in mode 13, followed by the
program to display it.
00 00 00 F0 00
00 00 00 FF 00
FF FF FF FF F0
FF FF FF FF FF
FF FF FF FF F0
00 00 00 FF 00
00 00 00 F0 00
Example 9-9.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
char arrow[] = {
0x00,0x00,0x00,0xF0,0x00,
0x00,0x00,0x00,0xFF,0x00,
0xFF,0xFF,0xFF,0xFF,0xF0,
0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xF0,
0x00,0x00,0x00,0xFF,0x00,
0x00,0x00,0x00,0xF0,0x00
};
void main()
{
int old_mode;
if (fg_testmode(13,1) == 0) {
printf("This program requires a 320 ");
printf("x 200 EGA graphics mode.\n");
exit(1);
}
152 Fastgraph User's Guide
old_mode = fg_getmode();
fg_setmode(13);
fg_setclip(0,15,0,199);
fg_move(10,10);
fg_drwimage(arrow,5,7);
fg_move(10,20);
fg_clpimage(arrow,5,7);
fg_move(8,30);
fg_revimage(arrow,5,7);
fg_move(8,40);
fg_flpimage(arrow,5,7);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Pixel Run Maps
The bit maps used with the fg_drawmap, fg_drwimage, and related routines
can consume array space quite rapidly. This is especially true if the image
is large or contains many colors. For example, a mode-independent bit-mapped
image that occupies the entire screen in a 320 by 200 graphics mode requires
8,000 bytes of space per color. Fastgraph provides another mode-independent
image format called pixel run maps, which are more efficient in terms of
space. In pixel run maps, you store the entire image in a single array.
Pixel run maps are particularly useful for displaying static images such as
backgrounds.
Let's return to our familiar triangle example and show how we could use
a pixel run map to display it.
. . . . * . . . .
. . . * x * . . .
. . * x x x * . .
. * x x x x x * .
* * * * * * * * *
As before, the pixels indicated by an asterisk (*) are the triangle's
perimeter, while those indicated by an x represent its interior points. The
pixels shown as periods (.) are not part of the triangle itself, but they are
part of the pixel run map.
If we start at the lower left corner of the image and proceed to the
right, we could represent the first row of the image as nine pixels of color
"asterisk". Such a group of consecutive identically colored pixels is called
a pixel run, so a single pixel run describes the first row of the image. The
row above this one is a bit more complex. It consists of five pixel runs:
one pixel of color "period", followed by one of color "asterisk", then five
of color "x", one of color "asterisk", and finally one of color "period".
While we could construct separate pixel runs for each row of the image,
notice that three of the five rows in our triangle begin with the same color
pixel as the rightmost pixel in the previous row. Fastgraph's pixel run map
format lets you take advantage of this property by allowing pixel runs to
Chapter 9: Images and Image Management 153
wrap from one row to the next. This means we can represent the pixel run of
color "period" extending from the right side of the second row to the left
side of the third row as a single run of three pixels.
The Fastgraph routine fg_display displays an image stored as a pixel run
map. The fg_display routine expects three arguments. The first is an array
containing the pixel runs (passed by reference), the second is the number of
pixel runs in the array, and the third is the width in pixels of the image.
As with the other image display routines, the fg_display routine displays the
image such that its lower left corner is at the graphics cursor position on
the active video page. The pixel run array is of the following format:
[0] color for run 1
[1] count for run 1
[2] color for run 2
[3] count for run 2
.
.
.
[2n-2] color for run n
[2n-1] count for run n
Each color is a value between 0 and 255 specifying the color index for that
pixel run. Each count is a value between 0 and 255 specifying the length in
pixels of that pixel run. If a run is longer than 255 pixels, it must be
broken into two or more runs. For example, we could represent a pixel run of
length 265 as a run of length 255 followed by a run of length 10 of the same
color. Note also the array space in bytes needed to store a pixel run map is
twice the number of runs.
It requires 16 pixel runs to store our triangle image as a pixel run
map. If we want to display the perimeter pixels in color 1, the interior
pixels in color 2, and the filler area in color 7, the pixel run map would
contain 16 sets of (color,count) pairs: (1,9), (7,1), (1,1), (2,5), (1,1),
(7,3), (1,1), (2,3), (1,1), (7,5), (1,1), (2,1), (1,1), (7,7), (1,1), and
(7,4).
Example 9-10 uses the fg_display routine to display the triangle as a
pixel run map in a 320 by 200 graphics mode. The program displays the
triangle against a background of color 7, so the selection of color 7 for the
filler area was important. If some other color were chosen, the filler area
would not blend in with the background.
Example 9-10.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
154 Fastgraph User's Guide
char triangle[] = {
1,9, 7,1, 1,1, 2,5, 1,1, 7,3, 1,1, 2,3,
1,1, 7,5, 1,1, 2,1, 1,1, 7,7, 1,1, 7,4
};
void main()
{
int old_mode, new_mode;
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_setcolor(7);
fg_rect(0,319,0,199);
fg_move(156,101);
fg_display(triangle,16,9);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
If you have a pixel run map that only uses the first 16 color indices (0
to 15), you can use Fastgraph's packed pixel run map image format. This
format packs two color values into each color byte and thus needs 25% less
array space to store an image. The Fastgraph routine fg_displayp displays an
image stored as a packed pixel run map. It is the same as the fg_display
routine except for the structure of the pixel run array. Like fg_display,
the pixel run array used with fg_displayp is a list of pixel runs, but two
runs are packed into three bytes. In each such set of three bytes, the high
four bits of the first byte contain the color of the first run, and the low
four bits contain the color of the second run. The second byte contains the
length of the first run, and the third byte contains the length of the second
run.
The following diagram illustrates the format of the pixel run array used
with the fg_displayp routine. The image is assumed to contain n pixel runs,
where n is an even number. If n is odd, the index of the last element is
3n/2 (truncated) instead of 3n/2-1, and the low four bits of the last color
byte (that is, the color for pixel run n+1) are ignored.
7 4 3 0
[0] color for run 1 color for run 2
[1] count for run 1
[2] count for run 2
Chapter 9: Images and Image Management 155
[3] color for run 3 color for run 4
[4] count for run 3
[5] count for run 4
.
.
.
[3n/2-3] color for run n-1 color for run n
[3n/2-2] count for run n-1
[3n/2-1] count for run n
The structure of the packed pixel run array allows for color values to
be between 0 and 15, and pixel run lengths to be between 0 and 255. The
array space in bytes needed to store a packed pixel run map is 1.5 times the
number of runs, compared to twice the number of runs for the standard pixel
run format.
Example 9-11 is the same as example 9-10, but it uses fg_displayp rather
than fg_display to display the image. Note the use of hexadecimal numbers
for defining the packed color values, which of course is not necessary but
certainly easier to read than expressing the quantities as decimal numbers.
Example 9-11.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
char triangle[] = {
0x17,9,1, 0x12,1,5, 0x17,1,3, 0x12,1,3,
0x17,1,5, 0x12,1,1, 0x17,1,7, 0x17,1,4
};
void main()
{
int old_mode, new_mode;
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_setcolor(7);
fg_rect(0,319,0,199);
fg_move(156,101);
156 Fastgraph User's Guide
fg_displayp(triangle,16,9);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Both the fg_display and fg_displayp routines require the pixel run image
to be stored in an array. In examples 9-10 and 9-11, the image is defined
within the program itself. However, if the image is stored in a file, it
must first be read into the pixel run array. Example 9-12 demonstrates this
procedure. The program displays two images stored in files, one in standard
pixel run format and the other in packed pixel run format. Each image is a
picture of the sea floor and some coral, as might be used for the background
in an aquarium. The program runs in a 320 by 200 graphics mode, and the
image fills the entire screen. It is assumed the image files contain the
list of pixel runs as a single byte stream that does not include embedded
characters such as carriage returns or line feeds.
The first image, in standard pixel run format, is in the file coral.spr.
Note the program must open the file for reading in binary mode ("rb" in the
call to fopen). The program reads the file's entire contents into the
pixel_runs array, whose size must be at least as large as the file size.
Because the image is stored in standard pixel run format, the number of pixel
runs is one-half the file size. The program then uses the fg_move routine to
establish the lower left corner of the screen as the graphics cursor position
and then calls fg_display to display the image. As mentioned earlier, the
image fills the entire screen, so its width is 320 pixels.
After waiting for a keystroke, the program similarly displays the second
image. This image is in the file coral.ppr and is stored in packed pixel run
format. Because the image is packed, the number of pixel runs is two-thirds
the file size. The program then clears the previous image from the screen
and calls fg_displayp to display the image. After another keystroke, the
program restores the original video mode and screen attributes and returns to
DOS.
Example 9-12.
#include <fastgraf.h>
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
char pixel_runs[20000];
void main()
{
long filelength();
FILE *stream;
int file_size, run_count;
int old_mode, new_mode;
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
Chapter 9: Images and Image Management 157
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
stream = fopen("coral.spr","rb");
file_size = (int)(filelength(fileno(stream)));
fread(pixel_runs,sizeof(char),file_size,stream);
fclose(stream);
run_count = file_size / 2;
fg_move(0,199);
fg_display(pixel_runs,run_count,320);
fg_waitkey();
stream = fopen("coral.ppr","rb");
file_size = (int)(filelength(fileno(stream)));
fread(pixel_runs,sizeof(char),file_size,stream);
fclose(stream);
run_count = file_size / 3 * 2;
fg_erase();
fg_displayp(pixel_runs,run_count,320);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Another Fastgraph routine, fg_dispfile, displays an image directly from
a file. This eliminates the need to read the file contents into an array
before displaying the image, and it also eliminates the need to compute the
number of pixel runs in the image. The fg_dispfile routine can display
images stored in either standard or packed pixel run images. The first of
its three arguments is the name of the file containing the image (it may
include a path name). The file name must be terminated with a null
character, so QuickBASIC, FORTRAN, and Turbo Pascal programmers will need to
store a zero byte as the last character of the file name string. The second
argument is the width in pixels of the image, and the third argument defines
the image format (that is, standard or packed). As with fg_display and
fg_displayp, the fg_dispfile routine displays the image such that its lower
left corner is at the graphics cursor position.
Example 9-13 illustrates how to use the fg_dispfile routine to display
an image stored in a pixel run file. It is functionally identical to example
9-12, but it is much simpler because it uses fg_dispfile instead of fg_display
and fg_displayp to display the images. The value of fg_dispfile's third
argument tells Fastgraph the image format. A value of 0 indicates the file
contains an image in standard pixel run format, while a value of 1 indicates
an image in packed pixel run format. As in example 9-12, the image files are
assumed to contain the list of pixel runs as a single byte stream that does
not include embedded characters such as carriage returns or line feeds.
Example 9-13.
#include <fastgraf.h>
158 Fastgraph User's Guide
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int old_mode, new_mode;
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_move(0,199);
fg_dispfile("coral.spr",320,0);
fg_waitkey();
fg_erase();
fg_dispfile("coral.ppr",320,1);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
To display the image, the fg_dispfile routine tries to allocate enough
dynamic memory to read the entire file. If it is unable to do so, it
allocates the available memory and displays the image in more than one pass.
In either case, Fastgraph deallocates the memory after fg_dispfile displays
the image.
The SNAPSHOT utility distributed with Fastgraph is a terminate and stay
resident program (TSR) that can capture graphics mode screen images and save
them in standard pixel run files. Thus, you can easily create files with
SNAPSHOT and display them with the fg_dispfile routine. Another TSR utility,
GrabRGB, is useful for capturing RGB color values from 256-color images.
Appendix A contains complete descriptions of the SNAPSHOT and GrabRGB
utilities.
Display Patterns
Examples 9-11, 9-12, and 9-13 work well in the graphics video modes with
16 or 256 available colors. However, in the four-color CGA graphics modes
the resulting image is not too good because of our limited color choices, and
it would look even worse in the Hercules graphics mode. The Fastgraph
routine fg_pattern allows you to associate a dither pattern (actually, any
pixel sequence) with one of Fastgraph's 256 color indices appearing in a
pixel run map. When displaying a pixel run map (with fg_display,
fg_displayp, or fg_dispfile), Fastgraph will use the pattern associated with
that color index instead of displaying the color itself.
Chapter 9: Images and Image Management 159
The fg_pattern routine requires two integer arguments -- a color index
(between 0 and 255) and the display pattern defined for that color index. A
display pattern's structure resembles the structure of video memory and is
thus dependent on the current video mode. The following sections list the
initial display patterns and explain how to construct new display patterns
for different graphics video modes.
CGA four-color graphics modes
In the four-color CGA graphics modes (modes 4 and 5), the display
pattern is a 16-bit quantity consisting of an 8-bit shift count followed by
an 8-bit pixel pattern. Each pixel assumes a value between 0 and 3, so the
pattern represents four pixels. In even-numbered pixel rows, Fastgraph uses
the pixel pattern itself. In odd-numbered pixel rows, Fastgraph rotates the
original pattern to the left by the number of bits specified by the shift
count.
For example, if we are using the default CGA color palette, we could
create a lighter shade of cyan by alternating cyan pixels (color 1, 01
binary) with white pixels (color 3, 11 binary), as shown below.
01 11 01 11
If we convert this pixel pattern to its hexadecimal equivalent, we get the
value 77.
To complete the display pattern, we need to determine the shift count.
If we use a shift count of zero, the resulting display will simply be a
series of cyan and white vertical lines. What we really need is a
checkerboard effect where a white pixel is above and below each cyan pixel,
and vice versa. If we rotate the pattern one pixel (two bits) to the left,
we will achieve the desired effect. That is, a shift count of two produces
the following pixel patterns:
even-numbered rows 01 11 01 11
odd-numbered rows 11 01 11 01
Combining the shift count with the pixel pattern yields the display pattern
0277 hex. The shift count is normally a multiple of two; note that a zero
shift count results in the same pattern being applied to all pixel rows.
For the CGA four-color graphics modes, the fg_setmode routine
establishes the following initial display patterns:
color shift count hexadecimal
index and pattern equivalent
0 0 00000000 0000
1 0 01010101 0055
2 0 10101010 00AA
3 0 11111111 00FF
160 Fastgraph User's Guide
These values are repeated as necessary to define color indices 4 to 255.
That is, colors 4, 8, 12, ... , 252 use the same defaults as color 0. Colors
5, 9, 13, ... , 253 use the same defaults as color 1, and so forth. Also
note that pattern 0000 represents four pixels of color 0, 0055 represents
four pixels of color 1, 00AA represents four pixels of color 2, and 00FF
represents four pixels of color 3.
CGA two-color graphics mode
In the two-color CGA graphics mode (mode 6), the display pattern is also
a 16-bit quantity consisting of an 8-bit shift count followed by an 8-bit
pixel pattern. Each pixel assumes the value 0 or 1, so the pattern
represents eight pixels. In even-numbered pixel rows, Fastgraph uses the
pixel pattern itself. In odd-numbered pixel rows, Fastgraph rotates the
original pattern to the left by the number of bits specified by the shift
count.
For example, we could create a lighter shade of white by alternating
black pixels (color 0) with white pixels (color 1), as shown below.
0 1 0 1 0 1 0 1
If we convert this pixel pattern to its hexadecimal equivalent, we get the
value 55.
To complete the display pattern, we need to determine the shift count.
We must rotate the pattern one pixel (one bit) to the left to achieve the
checkerboard effect as in the CGA four color graphics modes. That is, a
shift count of one produces the following pixel patterns:
even-numbered rows 0 1 0 1 0 1 0 1
odd-numbered rows 1 0 1 0 1 0 1 0
Combining the shift count with the pixel pattern yields the display pattern
0155 hex.
For the CGA two-color graphics mode, the fg_setmode routine establishes
the initial display patterns such that all even-numbered color indices are
assigned the value 0000, while all odd-numbered color indices are assigned
the value 00FF. Note that pattern 0000 represents eight pixels of color 0,
and 00FF represents eight pixels of color 1.
Tandy/PCjr 16-color graphics mode
In the Tandy/PCjr 16-color graphics mode (mode 9), the display pattern
is also 16-bit quantity consisting of an 8-bit shift count followed by an 8-
bit pixel pattern. Each pixel assumes a value between 0 and 15, so the
pattern represents two pixels. In even-numbered pixel rows, Fastgraph uses
the pixel pattern itself. In odd-numbered pixel rows, Fastgraph rotates the
original pattern to the left by the number of bits specified by the shift
count.
For example, we could create a lighter shade of blue by alternating blue
pixels (color 1, 0001 binary) with white pixels (color 15, 1111 binary), as
shown below.
Chapter 9: Images and Image Management 161
0001 1111
If we convert this pixel pattern to its hexadecimal equivalent, we get the
value 1F.
To complete the display pattern, we need to determine the shift count.
Using the same process as in the CGA graphics modes, we must rotate the
pattern one pixel (four bits) to the left to achieve the checkerboard effect.
That is, a shift count of four produces the following pixel patterns:
even-numbered rows 0001 1111
odd-numbered rows 1111 0001
Combining the shift count with the pixel pattern yields the display pattern
041F hex. The shift count is normally zero or four; note that a zero shift
count results in the same pattern being applied to all pixel rows.
For the Tandy/PCjr 16-color graphics modes, the fg_setmode routine
establishes the initial display patterns such that color 0 is assigned the
value 0000 (two pixels of color 0), color 1 is assigned the value 0011 (two
pixels of color 1), color 2 is assigned the value 0022 (two pixels of color
2), and so forth. These values are repeated as necessary to define color
indices 16 to 255. That is, colors 0, 16, 32, ... , 240 use the same
defaults as color 0. Colors 1, 17, 33, ... , 241 use the same defaults as
color 1, and so forth.
Hercules graphics modes
The structure of the display patterns for the Hercules graphics modes
(modes 11 and 12) is the same as two of the CGA graphics modes. For the
standard Hercules graphics mode (mode 11), please refer to the discussion of
CGA two-color (mode 6) display patterns on page 160. For the low-resolution
Hercules graphics mode (mode 12), please refer to the discussion of the CGA
four-color (mode 4) display patterns on page 159.
EGA graphics modes
In the EGA graphics modes (modes 13 to 16), the display pattern is an 8-
bit quantity consisting of two 4-bit color values (for consistency with the
other video modes, we still pass the display pattern as a 16-bit quantity).
Each pixel assumes a value between 0 and 15 (0 and 5 in the EGA monochrome
graphics mode), so the pattern represents two pixels. In even-numbered pixel
rows, Fastgraph uses the pixel pattern itself. In odd-numbered pixel rows,
Fastgraph rotates the original pattern one pixel (four bits) to the left.
For example, we could create a lighter shade of blue by alternating blue
pixels (color 1, 0001 binary) with white pixels (color 15, 1111 binary), as
shown below.
0001 1111
If we convert this pixel pattern to its hexadecimal equivalent, we get the
value 1F. The implied four-bit shift count produces the following pixel
patterns:
162 Fastgraph User's Guide
even-numbered rows 0001 1111
odd-numbered rows 1111 0001
Extending the pixel pattern to a 16-bit quantify yields the display pattern
001F hex.
For the EGA and VGA 16-color graphics modes, the fg_setmode routine
establishes the initial display patterns such that color 0 is assigned the
value 0000 (two pixels of color 0), color 1 is assigned the value 0011 (two
pixels of color 1), color 2 is assigned the value 0022 (two pixels of color
2), and so forth. These values are repeated as necessary to define color
indices 16 to 255. That is, colors 0, 16, 32, ... , 240 use the same
defaults as color 0. Colors 1, 17, 33, ... , 241 use the same defaults as
color 1, and so forth.
MCGA/VGA 2-color graphics mode
In the two-color MCGA/VGA graphics mode (mode 17), the display pattern
is a 2-bit quantity consisting of two 1-bit color values (for consistency
with the other video modes, we still pass the display pattern as a 16-bit
quantity). Each pixel assumes the value 0 or 1, so the pattern represents
two pixels. In even-numbered pixel rows, Fastgraph uses the pixel pattern
itself. In odd-numbered pixel rows, Fastgraph rotates the original pattern
one pixel (one bit) to the left.
For example, we could create a lighter shade of white by alternating
black pixels (color 0) with white pixels (color 1), as shown below.
0 1
If we convert this pixel pattern to its hexadecimal equivalent, we get the
value 01. The implied one-bit shift count produces the following pixel
patterns:
even-numbered rows 0 1
odd-numbered rows 1 0
Extending the pixel pattern to a 16-bit quantity yields the display pattern
0001 hex.
For the MCGA/VGA two-color graphics mode, the fg_setmode routine
establishes the initial display patterns such that all even-numbered color
indices are assigned the value 0000 (two pixels of color 0), while all odd-
numbered color indices are assigned the value 0003 (11 binary, or two pixels
of color 1).
VGA 16-color graphics mode
The structure of the display patterns for the VGA 16-color graphics mode
(mode 18) is the same as that of the EGA graphics modes. A discussion of EGA
display patterns appears on page 161.
Chapter 9: Images and Image Management 163
MCGA and VGA 256-color graphics modes
The MCGA and VGA 256-color graphics modes (modes 19 through 23) offer
262,144 different colors, so dithering is seldom (if ever) required. For
this reason, the fg_pattern routine has no effect in these video modes.
An example
Example 9-14 illustrates the use of display patterns in several graphics
modes. This program runs in any 320 by 200 color graphics mode and displays
the coral image in packed pixel run format, as in example 9-13, but it
redefines one or more of the color indices. If the program runs in the
standard CGA four-color mode (mode 4), it redefines the first 16 display
patterns using the fg_pattern routine and the values in the CGApatterns
array. In the Tandy/PCjr 16-color graphics mode (mode 9) and the EGA low-
resolution graphics mode (mode 13), the program redefines color index 15 to
produce an alternating gray and bright white dither pattern. In the MCGA
256-color mode (mode 19), display patterns are not available, so the program
uses fg_setrgb to define color index 15 as slightly darker shade of gray than
the default for color 7.
Example 9-14.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
int CGApatterns[] = {
0x0000,0x00FF,0x00FF,0x00FF,
0x02BB,0x0000,0x0222,0x0255,
0x00FF,0x00FF,0x00FF,0x0055,
0x00AA,0x00AA,0x00FF,0x0277
};
void main()
{
int color;
int old_mode, new_mode;
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
if (new_mode == 4) {
fg_palette(0,0);
for (color = 0; color < 16; color++)
fg_pattern(color,CGApatterns[color]);
}
else if (new_mode == 9 || new_mode == 13)
164 Fastgraph User's Guide
fg_pattern(15,0x04F7);
else
fg_setrgb(15,38,38,38);
fg_move(0,199);
fg_dispfile("coral.ppr",320,1);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
PCX Images
The PCX file format was originally developed by ZSoft Corporation for
their commercial paint program, PC Paintbrush. It has evolved into one of
the more popular image file formats because so many products can read PCX
files to at least some extent. Fastgraph includes routines for displaying
and creating PCX files.
The fg_disppcx routine displays an image stored in a PCX file. It
positions the image such that its upper left corner is at the graphics cursor
position on the active video page. The first argument to fg_disppcx is the
name of the PCX file (it may include a path name), and its second argument is
a 16-bit mask that controls how the image is displayed. The file name must
be terminated with a null character, so QuickBASIC, FORTRAN, and Turbo Pascal
programmers will need to store a zero byte as the last character of the file
name string. In the current version of Fastgraph, only the low-order bit
(bit 0) of the bit mask argument is meaningful. If the bit is set,
fg_disppcx will use the current palette settings. If it is zero, fg_disppcx
will use the palette values stored in the PCX file. All other bits are
reserved and should be zero. The fg_disppcx routine returns a value of 0 if
successful, 1 if the specified file wasn't found, and 2 if the file is not a
PCX file.
The fg_makepcx routine creates a PCX file from the specified rectangular
region of the active video page. Its first four arguments define the minimum
x, maximum x, minimum y, and maximum y screen space coordinates of the region
(the minimum x coordinate is reduced to a byte boundary if necessary). Its
fifth argument is the name of the PCX file to create (it may include a path
name). As with fg_disppcx, the file name must be terminated with a null
character. If an identically named file exists, it is overwritten. The
fg_makepcx routine returns a value of 0 if successful, and 1 if the PCX file
was not created.
Example 9-15 uses the fg_disppcx and fg_makepcx routines to create a new
PCX file from selected rows of an existing 256-color 320 x 200 PCX file. As
written, the program uses the file CORAL.PCX to create NEW.PCX, but it could
easily be extended to work with any PCX files. The call to fg_disppcx
displays the contents of CORAL.PCX using the palette settings defined in the
PCX file. After waiting for a keystroke, the program calls fg_makepcx to
create a PCX file named NEW.PCX from pixel rows 80 through 99 of the original
image. In case the program encounters any problems, it prints an error
message before exiting.
Chapter 9: Images and Image Management 165
Example 9-15.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int old_mode;
int read_status, write_status;
if (fg_testmode(19,1) == 0) {
printf("This program requires a 320 ");
printf("x 200 MCGA graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(19);
read_status = fg_disppcx("coral.pcx",0);
fg_waitkey();
if (read_status == 0)
write_status = fg_makepcx(0,319,80,99,"new.pcx");
else
write_status = 1;
fg_setmode(old_mode);
fg_reset();
if (read_status == 1)
printf("CORAL.PCX not found.\n");
else if (read_status == 2)
printf("CORAL.PCX is not a PCX file.\n");
if (write_status == 1)
printf("NEW.PCX not created.\n");
}
Because their structure parallels the structure of video memory, PCX
files are specific to certain video modes. The following table summarizes
the compatible video modes for PCX files.
If PCX file was You can display
created in mode it in these modes
4 or 5 4 or 5
6 or 11 6, 11, 13 to 18
9 9
13 to 18 13 to 18
19 19 to 23
Displaying a PCX file at a lower resolution (for example, a 640x480 PCX file
at 320x200) will truncate the display on the right and on the bottom. This
effectively displays the upper left corner of the PCX file. If you try to
166 Fastgraph User's Guide
display a PCX file in an incompatible video mode, fg_disppcx will still
display something, but it will be garbled.
In the Tandy/PCjr 16-color graphics mode (mode 9) and the native EGA
graphics modes (modes 13 through 16), the palette registers are not readable.
Hence, fg_makepcx will use the default palette settings when used in these
video modes. In 640-column EGA modes, you can get around this problem by
creating PCX files in mode 18 and only using the first 200 or 350 pixel rows.
PCX files created in mode 18 preserve the current palette settings and will
display properly in EGA modes. The fg_disppcx and fg_makepcx routines have
no effect in text video modes or in the Hercules low-resolution graphics
mode.
Masking Maps
It is not possible to include color 0 pixels in an image displayed with
the fg_drwimage, fg_clpimage, fg_revimage, or fg_flpimage routines. This is
because these routines consider color 0 pixels to be transparent, which means
such pixels do not affect the corresponding pixels in video memory. There
are times, however, when you will want color 0 pixels to be destructive, or
replace the video memory contents.
Consider again the arrow image of example 9-8 (see page 149). In this
example, we displayed a bright white (color 3) arrow against a black (color
0) background in the standard CGA four-color graphics mode. Suppose, though,
that we want to do just the opposite -- display a black (color 0) arrow
against a bright white (color 3) background. Example 9-9 (see page 151) does
this in an EGA graphics mode, but how would we display the black arrow in a
CGA graphics mode? We could of course use the fg_drawmap routine or one of
the routines for displaying pixel run maps, but fg_drawmap does not support
clipping or reversing an image. There are, however, four Fastgraph routines
designed just for this purpose. These routines are fg_drawmask, fg_clipmask,
fg_revmask, and fg_flipmask.
Each of these routines uses a data structure called a masking map. A
masking map is similar in structure to a pixel run map, but it does not
include any information about colors. Instead, it consists of a series of
pixel runs that alternate between protected and unprotected pixels. An
example might best clarify this.
Once again, here is the arrow image of examples 9-8 and 9-9.
. . . . . . * . . .
. . . . . . * * . .
* * * * * * * * * .
* * * * * * * * * *
* * * * * * * * * .
. . . . . . * * . .
. . . . . . * . . .
This time, though, we want the arrow to appear in color 0. Put another way,
we need the "period" pixels (.) to protect video memory, while we want the
"asterisk" pixels (*) to zero video memory. Looking at this problem from the
perspective of a pixel run map, we have an alternating series of "protect"
and "zero" runs. We don't need any information about pixel colors, just
whether to protect or to zero video memory.
Chapter 9: Images and Image Management 167
This is precisely the structure of a masking map. Starting from the
lower left corner of the image and proceeding to the right, wrapping up to
the next row when needed, we could represent this image as a masking map with
6 protected pixels, 1 zeroed pixel, 9 protected pixels, 2 zeroed pixels, and
so on. In general, the structure of a masking map is as follows.
[1] length of 1st protect run
[2] length of 1st zero run
[3] length of 2nd protect run
[4] length of 2nd zero run
.
.
.
[n-2] length of final protect run
[n-1] length of final zero run
Looking at this diagram, we see that the even-numbered array elements
hold the length of the "protect" runs, and the odd-numbered elements hold the
length of the "zero" runs. If you need the first run to be a "zero" run,
just include a "protect" run of length zero as the first element of the
array. If the final run is a "protect" run, you do not need to include a
zero-length "zero" run at the end of the array. Finally, if either type of
run exceeds 255 pixels, you'll need to split this into two or more pixel
runs. In this case, be sure to include a zero-length run of the other type
between the two array elements.
Example 9-16 illustrates the use of a masking map through the
fg_drawmask, fg_clipmask, fg_revmask, and fg_flipmask routines in the
standard CGA four-color graphics mode (mode 4) to draw a black (color 0)
arrow against a bright white background. These four routines are
respectively analogous to the fg_drwimage, fg_clpimage, fg_revimage, and
fg_flpimage routines, but they use masking maps rather than bit maps. The
first argument of each routine is the masking map array (passed by
reference), the second argument is the number of runs (that is, the number of
elements) in the masking map array, and the third argument is the width in
pixels of the image.
Example 9-16.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
char arrow[] = {6,1,9,2,2,9,1,19,7,2,8,1};
void main()
{
int old_mode;
168 Fastgraph User's Guide
if (fg_testmode(4,1) == 0) {
printf("This program requires a 320 ");
printf("x 200 CGA graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(4);
fg_setclip(0,15,0,199);
fg_setcolor(3);
fg_rect(0,319,0,199);
fg_move(10,10);
fg_drawmask(arrow,12,10);
fg_move(10,20);
fg_clipmask(arrow,12,10);
fg_move(10,30);
fg_revmask(arrow,12,10);
fg_move(10,40);
fg_flipmask(arrow,12,10);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
One of the more useful features of masking maps is the ability to clear
a portion of video memory before placing an image there. This technique
provides an efficient, simple way to include color 0 pixels in an image. It
is especially effective when displaying large or dithered images because the
masking map is typically much smaller than the bit map required by fg_drawmap
or its related routines. Example 9-17 illustrates this process in the
standard CGA four-color graphics mode (mode 4) by displaying our arrow image
against a colored background. In this example, the arrow has a bright white
(color 3) perimeter and a black (color 0) interior.
The program displays the arrow in two steps. It first uses fg_drawmask
to clear the video memory where the arrow will be displayed. It then draws
the arrow's perimeter using the fg_drwimage routine. The interior pixels in
the perimeter bit map are transparent, but since we just zeroed that video
memory, they appear in color 0. Note we could improve this example by
creating a smaller masking map that only applies to the rectangle inscribing
the arrow's interior. That is, we don't need to zero the video memory under
the arrow's perimeter because we will immediately display other pixels there.
Example 9-17.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
char arrow_white[] = {
0x00,0x0C,0x00, 0x00,0x0F,0x00, 0xFF,0xFC,0xC0,
0xC0,0x00,0x30, 0xFF,0xFC,0xC0, 0x00,0x0F,0x00,
0x00,0x0C,0x00
};
Chapter 9: Images and Image Management 169
char arrow_black[] = {6,1,9,2,2,9,1,19,7,2,8,1};
void main()
{
int old_mode;
if (fg_testmode(4,1) == 0) {
printf("This program requires a 320 ");
printf("x 200 CGA graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(4);
fg_setcolor(2);
fg_rect(0,319,0,199);
fg_move(10,10);
fg_drawmask(arrow_black,12,10);
fg_drwimage(arrow_white,3,7);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Retrieving Images
Sometimes it is necessary to retrieve an image from video memory and
store it in one or more bit map arrays. Fastgraph includes two routines,
fg_getmap and fg_getimage, for this purpose. The fg_getmap routine retrieves
pixels of the current color index and stores them in the mode-independent bit
map format used by fg_drawmap. The fg_getimage routine retrieves an image
and stores it in the mode-specific bit map format used by fg_drwimage,
fg_clpimage, fg_revimage, and fg_flpimage. The arguments to fg_getmap and
fg_getimage are respectively analogous to those of fg_drawmap and
fg_drwimage: the first is an array (passed by reference) to receive the bit
map, the second is the width of the bit map in bytes, and the last is the
height of the bit map in pixel rows. With either routine, the graphics
cursor position on the active video page defines the lower left corner of the
image to retrieve.
If we want to use the fg_getmap routine to retrieve an image containing
more than one color, we must call the routine once per color. In this case
we'll usually want to pass different bit map arrays to fg_getmap (or perhaps
different offsets into the same array). This might seem unusual at first,
but it parallels the behavior of the fg_drawmap routine. That is, to display
a multicolor image using fg_drawmap, we must call it once for each color in
the image.
Example 9-18 demonstrates a typical use of the fg_getmap routine. The
program displays the word "text" in the upper left corner of the screen using
a 320 by 200 graphics mode. It uses fg_getmap to retrieve the word as an
170 Fastgraph User's Guide
image and then displays it in a new position with the fg_drawmap routine.
Let's look at the program now, and afterward we'll more closely examine the
screen coordinates and the structure of the bit map array.
Example 9-18.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
char bitmap[32];
int old_mode, new_mode;
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_setcolor(9);
fg_text("text",4);
fg_waitkey();
fg_move(0,7);
fg_getmap(bitmap,4,8);
fg_move(4,15);
fg_drawmap(bitmap,4,8);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
In all 320 by 200 graphics video modes, individual characters are 8
pixels wide and 8 pixels high. This means the lower left corner of the (0,0)
character cell is referenced by the screen coordinates (0,7). Hence, these
are the coordinates of the first call to fg_move. The image retrieved in
example 9-18 is four characters long (32 pixels wide), so we need a bit map
array capable of holding 8 rows of 32 pixels (4 bytes) each. Our bit map
array is therefore a 32-byte array, logically structured to have 4 columns
and 8 rows. These values are the width and height arguments passed to
fg_getmap and fg_drawmap.
After it retrieves the image, example 9-18 displays it one line below
and one-half character cell (four pixels) to the right of its original
position. In other words, the program displays the image four pixels to the
right of the (1,0) character cell. The lower left corner of that cell is
referenced by the screen coordinates (0,15), so the image should appear at
the position (4,15). These are the coordinates of the second call to
fg_move.
Chapter 9: Images and Image Management 171
Example 9-19 illustrates the use of the fg_getmap and fg_drawmap
routines to retrieve and display two-color image. This example is similar to
example 9-18, but this program first draws a rectangle in the upper left
corner of the screen and then displays the word "text" on top of the
rectangle in a different color. Each character in a 320 by 200 graphics
video mode is 8 pixels wide and 8 pixels high, so the rectangle must be 32
pixels wide (4 characters times 8 pixels per character) and 8 pixels high.
The image to retrieve will be the same size as the rectangle.
The image retrieved in example 9-18 required a 32-byte array, logically
structured to have 4 columns and 8 rows. Example 9-19 will retrieve an image
of the same structure, but the image contains two colors instead of just one.
This means we need two 32-byte arrays, one for each color, to hold the image.
We could instead use a single 64-byte array and pass an offset into that
array (specifically, &bitmap[32]) for processing the second color.
Example 9-19.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
char bitmap1[32], bitmap2[32];
int old_mode, new_mode;
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_setcolor(7);
fg_rect(0,31,0,7);
fg_setcolor(9);
fg_text("text",4);
fg_waitkey();
fg_move(0,7);
fg_setcolor(7);
fg_getmap(bitmap1,4,8);
fg_setcolor(9);
fg_getmap(bitmap2,4,8);
fg_move(4,15);
fg_setcolor(7);
fg_drawmap(bitmap1,4,8);
fg_setcolor(9);
fg_drawmap(bitmap2,4,8);
fg_waitkey();
172 Fastgraph User's Guide
fg_setmode(old_mode);
fg_reset();
}
Example 9-20 is similar to example 9-19, but it uses fg_getimage and
fg_drwimage instead of fg_getmap and fg_drawmap to retrieve and display the
image. That is, it uses the mode-specific rather than the mode-independent
image retrieval and display routines. When using the mode-specific routines,
the size of the bit map needed to hold the image depends on the video mode.
For programs that run in only one video mode, bit map widths are constant,
but when a program must run in several video modes, the width is variable.
The Fastgraph routine fg_imagesiz computes the number of bytes required to
store a mode-specific bit-mapped image of specified dimensions. Its two
integer arguments specify the image width and height in pixels.
The program computes the image width in bytes by passing a height of 1
to fg_imagesiz. The size of the bit map array in example 9-20 is 256 bytes,
the size required in the MCGA graphics mode (32 bytes times 8 bytes). The
other video modes require less storage, so in these modes only a portion of
the bit map array will actually be used. The image width is then used in the
calls to both fg_getimage and fg_drwimage.
Example 9-20.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
char bitmap[256];
int old_mode, new_mode;
int width;
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
width = (int)fg_imagesiz(32,1);
fg_setcolor(7);
fg_rect(0,31,0,7);
fg_setcolor(9);
fg_text("text",4);
fg_waitkey();
fg_move(0,7);
fg_getimage(bitmap,width,8);
fg_move(4,15);
fg_drwimage(bitmap,width,8);
fg_waitkey();
Chapter 9: Images and Image Management 173
fg_setmode(old_mode);
fg_reset();
}
While this example used an array to store the image, it's usually preferable
to allocate dynamic memory for this purpose. We could have done this in
example 9-20 by calling fg_imagesiz with arguments of 32 (width) and 8
(height). The routine's return value would then tell us the number of bytes
we would need to allocate.
We also can use the fg_getimage routine to retrieve images in text video
modes. In text modes, however, there are a few differences we must consider
when using fg_getimage. First, the text cursor position, not the graphics
cursor position, specifies the lower left corner of the image. Hence, we
must use the fg_locate routine instead of fg_move to define the image
location. Second, the image width is always twice the number of characters
per image row (that is, for each character we have a character byte and an
attribute byte). The fg_getmap routine has no effect when used in a text
video mode.
Example 9-21 shows a simple use of fg_getimage in text modes. This
program is similar to example 9-20, but it runs in an 80-column text mode
rather than a 320 by 200 graphics mode. As before, the program will retrieve
the four characters "text" as an image from the upper left corner of the
screen and then display it in a different location. Because the image
consists of four characters in one row, the image width is 8 bytes and the
image height is 1.
Example 9-21.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int old_mode;
char image[8];
old_mode = fg_getmode();
if (fg_testmode(3,1))
fg_setmode(3);
else if (fg_testmode(7,1))
fg_setmode(7);
else {
printf("This program requires\n");
printf("an 80-column display.\n");
exit(1);
}
fg_cursor(0);
fg_setattr(9,7,0);
fg_text("text",4);
174 Fastgraph User's Guide
fg_waitkey();
fg_locate(0,0);
fg_getimage(image,8,1);
fg_locate(1,1);
fg_drwimage(image,8,1);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Byte Boundaries
Video memory, like standard random-access memory, is divided into units
called bytes. In text modes, each byte holds either a character or an
attribute. In graphics modes, each byte of video memory holds one or more
horizontally contiguous pixels. If two adjacent horizontal pixels are stored
in different bytes, then we say that a byte boundary exists between the two
pixels.
The number of pixels per byte depends on the video mode being used, so
the byte boundaries also depend on the video mode. That is, a byte boundary
in a CGA graphics mode is not necessarily a byte boundary in an EGA graphics
mode. The following table summarizes the number of pixels per byte of video
memory and the byte boundary sequences for each supported graphics video
mode. Note that any horizontal coordinate whose value is a multiple of 8 is
always a byte boundary, regardless of the video mode.
mode pixels horizontal coordinates
number per byte of byte boundaries
4 4 0, 4, 8, 12, ... , 316
5 4 0, 4, 8, 12, ... , 316
6 8 0, 8, 16, 24, ... , 632
9 2 0, 2, 4, 6, ... , 318
11 8 0, 8, 16, 24, ... , 712
12 4 0, 4, 8, 12, ... , 316
13 8 0, 8, 16, 24, ... , 312
14 8 0, 8, 16, 24, ... , 632
15 8 0, 8, 16, 24, ... , 632
16 8 0, 8, 16, 24, ... , 632
17 8 0, 8, 16, 24, ... , 632
18 8 0, 8, 16, 24, ... , 632
19 1 0, 1, 2, 3, ... , 319
20 1 0, 1, 2, 3, ... , 319
21 1 0, 1, 2, 3, ... , 319
22 1 0, 1, 2, 3, ... , 319
23 1 0, 1, 2, 3, ... , 319
Chapter 9: Images and Image Management 175
Image Transfer Routines
The Fastgraph routines described in this section transfer images between
areas of video memory. These routines are often used in animation sequences
requiring high-performance graphics, so they must be as efficient as
possible. To this end, Fastgraph will force their horizontal pixel
coordinates to byte boundaries, which eliminates the need to process any
pixels individually. Fastgraph accomplishes this by reducing minimum
horizontal coordinates to a byte boundary and extending maximum horizontal
coordinates to the last pixel in a video memory byte. Note that since we are
talking about pixel coordinates and not character cells, the coordinate
modification only occurs in graphics video modes.
An example might best help explain this important feature. The CGA
four-color graphics modes (modes 4 and 5) store four pixels in each byte of
video memory. This means the byte boundaries occur at multiples of four
pixels. Thus, when you use the image transfer routines in modes 4 and 5,
Fastgraph reduces minimum x coordinates to the next lower multiple of four.
Similarly, it extends their maximum x coordinates to the next higher multiple
of four, less one pixel. That is, if a minimum x coordinate is 7 and a
maximum x coordinate is 30, Fastgraph will modify these values to 4 and 31
respectively. If the x coordinates were originally 4 and 31, Fastgraph would
leave them unchanged. Note, too, that because each pixel in the MCGA and VGA
256-color graphics modes (modes 19 through 23) occupies a separate byte of
video memory, Fastgraph does not need to modify horizontal coordinates in
these video modes.
Fastgraph's simplest image transfer routine is fg_copypage, which we
introduced in Chapter 8. The fg_copypage routine transfers the entire
contents of one video page to another. The first argument is the number of
the source video page, and the second argument is the number of the
destination video page. The pages may be physical, virtual, or logical video
pages. If both the source and destination pages are logical pages, the pages
must exist in the same type of memory. For example, you cannot copy a
logical page in extended memory to a logical page in conventional memory.
Example 9-22 illustrates the use of the fg_copypage routine in a 320 x
200 color graphics mode. The program displays the word "test" in the middle
of the visual page (page 0) and then uses fg_allocate to create a virtual
video page (page 1). The virtual page is needed in case the program is
running in a video mode with only one physical page. Next, the program uses
176 Fastgraph User's Guide
fg_copypage to transfer the visual page contents to page 1. After waiting
for a keystroke, the program erases the visual page, waits for another
keystroke, and copies the contents of page 1 back to the visual page. It
then releases the virtual page and exits.
Example 9-22.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int new_mode, old_mode;
new_mode = fg_bestmode(320,200,2);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_setcolor(7);
fg_rect(0,319,0,199);
fg_setcolor(9);
fg_locate(12,18);
fg_text("test",4);
fg_waitkey();
fg_allocate(1);
fg_copypage(0,1);
fg_erase();
fg_waitkey();
fg_copypage(1,0);
fg_waitkey();
fg_freepage(1);
fg_setmode(old_mode);
fg_reset();
}
Several of Fastgraph's image transfer routines reference a video page
called the hidden page. The Fastgraph routine fg_sethpage defines which
video page will be used as the hidden page. This routine takes as its only
argument an integer value specifying the hidden page number. If you are
using a virtual video page for the hidden page, you must call the fg_sethpage
routine after allocating that page. There is also a routine named
fg_gethpage that returns the hidden page number, as specified in the most
recent call to fg_sethpage, as its function value. The fg_gethpage routine
takes no arguments.
The next two image transfer routines we'll discuss are fg_save and
fg_restore. The fg_save routine transfers a rectangular region from the
Chapter 9: Images and Image Management 177
active video page (as defined in the most recent call to fg_setpage) to the
same position on the hidden video page (as defined in the most recent call to
fg_sethpage). The fg_restore routine performs the complementary task -- it
transfers a rectangular region from the hidden page to the active page. Each
of these routines requires four arguments that define the coordinates of the
region to transfer, in the order minimum x, maximum x, minimum y, and maximum
y. In text modes, the coordinates are expressed as character space
quantities (rows and columns). In graphics modes, they are expressed as
screen space values (pixels); the x coordinates are extended to byte
boundaries if required. There are also world space versions of these
routines named fg_savew and fg_restorew available in graphics modes.
Example 9-23 demonstrates the use of Fastgraph's fg_save, fg_restore,
and fg_sethpage routines in an 80-column text video mode. After establishing
the video mode (note the calls to fg_testmode specify that two video pages
are needed), the program fills the screen with text and then waits for a
keystroke. Following this, the program displays a small pop-up window
prompting for another keystroke. After waiting for the second keystroke, the
program erases the pop-up window by restoring the original screen contents,
and then waits for yet another keystroke before returning to DOS. We'll
present the program now, and afterward analyze it in detail.
Example 9-23.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int row;
int old_mode;
char string[17];
old_mode = fg_getmode();
if (fg_testmode(3,2))
fg_setmode(3);
else if (fg_testmode(7,2))
fg_setmode(7);
else {
printf("This program requires\n");
printf("an 80-column display.\n");
exit(1);
}
fg_cursor(0);
fg_setattr(9,7,0);
for (row = 0; row < 25; row++) {
sprintf(string," This is row %2d ",row);
fg_locate(row,0);
fg_text(string,16);
fg_text(string,16);
fg_text(string,16);
fg_text(string,16);
178 Fastgraph User's Guide
fg_text(string,16);
}
fg_waitkey();
fg_allocate(1);
fg_sethpage(1);
fg_save(32,47,11,13);
fg_setcolor(1);
fg_rect(32,47,11,13);
fg_setattr(15,1,0);
fg_locate(12,33);
fg_text("Press any key.",14);
fg_waitkey();
fg_restore(32,47,11,13);
fg_waitkey();
fg_freepage(1);
fg_setmode(old_mode);
fg_reset();
}
Example 9-23 first establishes the video mode and uses the fg_cursor
routine to make the BIOS cursor invisible. It then executes a for loop that
fills each row of the screen with the phrase "This is row n", where n is the
row number (between 0 and 24). Next, the program uses the fg_allocate
routine to create video page 1 as a virtual video page. This is needed in
case the program is running in mode 7, which has only one true page (if the
program is running in mode 3, the call to fg_allocate has no effect). The
program then makes page 1 the hidden page by calling the fg_sethpage routine.
After setting up the hidden video page, but before displaying the pop-up
window, example 9-23 uses the fg_save routine to save the current contents of
the area that the pop-up window will replace. The fg_save routine copies
this region to the hidden page. The program then displays the pop-up window
in the middle of the screen and leaves it there until a key is pressed. After
this, the program uses the fg_restore routine to replace the pop-up window
with the original contents of that region. This effectively erases the pop-up
window and restores the original screen. The program then waits for another
keystroke, after which it releases the virtual page and returns to DOS.
The next example, 9-24, is similar to example 9-23, but it runs in a 320
by 200 color graphics mode instead of a text mode. The main differences
between this program and example 9-23 are the use of 40-column text and the
use of screen space coordinates instead of character space coordinates in the
calls to fg_save, fg_restore, and fg_rect. Note that the call to fg_allocate
creates a virtual page if the program is running in modes 4, 9, or 19. In
mode 13, which has 8 true pages, the fg_allocate routine does nothing.
Example 9-24.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
Chapter 9: Images and Image Management 179
void main()
{
int row;
int new_mode, old_mode;
char string[21];
new_mode = fg_bestmode(320,200,2);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_setcolor(7);
fg_rect(0,319,0,199);
fg_setcolor(9);
for (row = 0; row < 25; row++) {
sprintf(string," This is row %2d ",row);
fg_locate(row,0);
fg_text(string,20);
fg_text(string,20);
}
fg_waitkey();
fg_allocate(1);
fg_sethpage(1);
fg_save(96,223,88,111);
fg_setcolor(1);
fg_rect(96,223,88,111);
fg_setcolor(15);
fg_locate(12,13);
fg_text("Press any key.",14);
fg_waitkey();
fg_restore(96,223,88,111);
fg_waitkey();
fg_freepage(1);
fg_setmode(old_mode);
fg_reset();
}
The fg_save and fg_restore routines each copy a rectangular region from
one video page to the same position on another video page. What if you need
to copy the region to a different position on another video page, or copy it
elsewhere on the same video page? Fastgraph provides a more general image
transfer routine named fg_transfer. The fg_transfer routine copies a
rectangular region on any video page to any position on any video page. Like
fg_save and fg_restore, the fg_transfer routine works in text and graphics
video modes. In graphics modes, fg_transfer extends its x coordinates to
byte boundaries if necessary.
The fg_transfer routine requires eight integer arguments. The first
four arguments define the region to copy, in the same order as expected by
the fg_save and fg_restore routines. The next two arguments define the lower
180 Fastgraph User's Guide
left corner of the image destination, while the final two arguments
respectively specify the source and destination video page numbers. In
short, fg_transfer copies the specified region from the source page to the
specified position on the destination page.
Example 9-25 is the same as example 9-23, but it uses fg_transfer rather
than fg_save and fg_restore. We have arbitrarily chosen to copy the region
overwritten by the pop-up window to the lower left corner of the hidden page
(page 1). When we copy this region back to the visual page, we copy from the
lower left corner of the hidden page back to the original position on the
visual page (page 0). This sequence is shown in the following diagram.
(11,32) (11,47) (22,0) (22,15)
first call
This is row 11 ------------> This is row 11
This is row 12 This is row 12
This is row 13 <------------ This is row 13
second call
(13,32) (13,47) (24,0) (24,15)
visual page (0) hidden page (1)
To copy one region to a new position and then back to its original position,
note how we make the fifth and sixth arguments in the first call to
fg_transfer the same values as the first and fourth arguments in the second
call. Similarly, the fifth and sixth arguments in the second call must be
the same as the first and fourth arguments in the first call. Now, here is
example 9-25.
Example 9-25.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int row;
int old_mode;
char string[17];
old_mode = fg_getmode();
if (fg_testmode(3,2))
fg_setmode(3);
else if (fg_testmode(7,2))
fg_setmode(7);
else {
printf("This program requires\n");
printf("an 80-column display.\n");
exit(1);
}
fg_cursor(0);
fg_setattr(9,7,0);
Chapter 9: Images and Image Management 181
for (row = 0; row < 25; row++) {
sprintf(string," This is row %2d ",row);
fg_locate(row,0);
fg_text(string,16);
fg_text(string,16);
fg_text(string,16);
fg_text(string,16);
fg_text(string,16);
}
fg_waitkey();
fg_allocate(1);
fg_transfer(32,47,11,13,0,24,0,1);
fg_setcolor(1);
fg_rect(32,47,11,13);
fg_setattr(15,1,0);
fg_locate(12,33);
fg_text("Press any key.",14);
fg_waitkey();
fg_transfer(0,15,22,24,32,13,1,0);
fg_fg_waitkey();
fg_freepage(1);
fg_setmode(old_mode);
fg_reset();
}
Example 9-26 illustrates another use of the fg_transfer routine. This
example is functionally identical to example 9-18 (see page 170), but it uses
fg_transfer instead of fg_getmap and fg_drawmap. With the fg_transfer
routine, we eliminate the calls to fg_getmap and fg_drawmap, the two calls to
fg_move, and the 32-byte array needed to retrieve the image. As an added
bonus, using fg_transfer is much faster than the technique of example 9-18,
although we probably won't notice this gain with such a small image.
The image copied in example 9-26 is one row of four characters, so its
width in screen space is 32 pixels and its height is 8 pixels. Because the
image is in the upper left corner of the screen, the image boundaries are
xmin=0, xmax=31, ymin=0, and ymax=7. We want to move the image one-half
character cell (4 pixels) to the right and one row (8 pixels) down, so our
destination coordinates are x=4 (xmin+4) and y=15 (ymax+8). Also, we are
copying the image from one position to another on the visual page, so both
the source and destination pages are 0.
Example 9-26.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int old_mode, new_mode;
182 Fastgraph User's Guide
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_setcolor(9);
fg_text("text",4);
fg_waitkey();
fg_transfer(0,31,0,7,4,15,0,0);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Example 9-27 shows yet another application of the fg_transfer routine in
a graphics video mode. The program displays a rectangle in the upper left
quadrant of the screen and then centers the word "quadrant" inside the
rectangle. After waiting for a keystroke, the program uses fg_transfer to
first copy the upper left quadrant to the upper right quadrant. It then uses
fg_transfer again to copy the upper half of the screen to the lower half.
The result of this is the screen being filled with what was originally in the
upper left quadrant.
Example 9-27.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int new_mode, old_mode;
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_setcolor(7);
fg_rect(0,159,0,99);
fg_setcolor(9);
fg_locate(6,6);
fg_text("quadrant",8);
fg_waitkey();
Chapter 9: Images and Image Management 183
fg_transfer(0,159,0,99,160, 99,0,0);
fg_transfer(0,319,0,99, 0,199,0,0);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
The final routines pertaining to image transfer are fg_tcxfer and
fg_tcmask. The fg_tcxfer routine is similar to fg_transfer in that it copies
a rectangular region from one position to another, but fg_tcxfer allows you
to treat one or more colors as transparent (the name fg_tcxfer stands for
"transparent color transfer"). In other words, any pixel whose color value
is defined to be transparent is not copied to the destination area. The
fg_tcxfer routine's arguments are the same as for the fg_transfer routine, but
fg_tcxfer has no effect in text video modes. Because fg_tcxfer must examine
the color of individual pixels, it is not as fast as the fg_transfer routine.
The fg_tcmask routine defines which colors are considered transparent in
subsequent calls to fg_tcxfer. Its argument is an integer bit mask
(specifically, a 16-bit mask) where each bit indicates whether or not the
color is transparent. For example, if bit 0 (the rightmost bit) is set in
the mask, then color 0 will be transparent; if bit 0 is reset, color 0 will
not be transparent. Because the bit mask size is 16 bits, only the first 16
color values may be defined as transparent.
Example 9-28 illustrates the use of the fg_tcxfer and fg_tcmask routines.
This program is the same as example 9-27, except the color of the word
"quadrant" (color 9) is defined to be transparent, and fg_tcxfer is used in
place of fg_transfer. Because color 9 maps to color 1 in the CGA four-color
graphics mode (mode 4), we must define both colors 1 and 9 to be transparent
(remember, fg_tcmask considers actual color values transparent, not color
indices). The bit mask passed to fg_tcmask thus will be 0000 0010 0000 0010
binary, or 0202 hex. The result of this program is the same as example 9-27,
but the word "quadrant" appears in the background color (color 0) instead of
color 9 in the upper right, lower left, and lower right quadrants.
Example 9-28.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int new_mode, old_mode;
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
184 Fastgraph User's Guide
fg_setcolor(7);
fg_rect(0,159,0,99);
fg_setcolor(9);
fg_locate(6,6);
fg_text("quadrant",8);
fg_waitkey();
fg_tcmask(0x0202);
fg_tcxfer(0,159,0,99,160, 99,0,0);
fg_tcxfer(0,319,0,99, 0,199,0,0);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Summary of Image Display Routines
This section summarizes the functional descriptions of the Fastgraph
routines presented in this chapter. More detailed information about these
routines, including their arguments and return values, may be found in the
Fastgraph Reference Manual.
For all image display routines, images are positioned so their lower
left corner is at the graphics cursor position (or text cursor position for
those routines that also work in text video modes). For all image transfer
routines, Fastgraph extends the horizontal pixel coordinates to a byte
boundary when the routines are used in a graphics video mode.
FG_CLIPMASK displays a clipped image stored as a masking map. This
routine has no effect when used in a text video mode.
FG_CLPIMAGE displays a clipped image stored as a mode-specific bit map.
This routine has no effect when used in a text video mode.
FG_COPYPAGE transfers the contents of one video page to another. The
pages may be physical, virtual, or logical video pages. If both pages are
logical pages, they must exist in the same type of memory.
FG_DISPFILE displays an image stored in Fastgraph's standard or packed
pixel run format, where the image resides in an external file. This routine
has no effect when used in a text video mode.
FG_DISPLAY displays an image stored in Fastgraph's standard pixel run
format, where the image resides in an array. This routine has no effect when
used in a text video mode.
FG_DISPLAYP displays an image stored in Fastgraph's packed pixel run
format, where the image resides in an array. This routine has no effect when
used in a text video mode.
FG_DISPPCX displays an image stored in a PCX file. The image will be
positioned so its upper left corner is at the graphics cursor position of the
active video page.
Chapter 9: Images and Image Management 185
FG_DRAWMAP displays an image stored as a mode-independent bit map. This
routine has no effect when used in a text video mode.
FG_DRAWMASK displays an image stored as a masking map. This routine has
no effect when used in a text video mode.
FG_DRWIMAGE displays an image stored as a mode-specific bit map.
FG_FLIPMASK displays a reversed clipped image stored as a masking map.
This routine has no effect when used in a text video mode.
FG_FLPIMAGE displays a reversed clipped image stored as a mode-specific
bit map. This routine has no effect when used in a text video mode.
FG_GETHPAGE returns the hidden page number, as defined in the most
recent call to fg_sethpage.
FG_GETIMAGE retrieves an image as a mode-specific bit map.
FG_GETMAP retrieves an image as a mode-independent bit map. This
routine has no effect when used in a text video mode.
FG_IMAGESIZ determines the number of bytes required to store a mode-
specific bit-mapped image of specified dimensions.
FG_MAKEPCX creates a PCX file from the specified rectangular region of
the active video page. The region's extremes are expressed in screen space
units.
FG_PATTERN defines a display pattern for use with the fg_dispfile,
fg_display, or fg_displayp routines. This routine has no effect when used in
a text video mode.
FG_RESTORE copies an image from the hidden video page to the same
position on the active video page.
FG_RESTOREW is the same as fg_restore, but the image extremes are
specified as world space coordinates.
FG_REVIMAGE displays a reversed image stored as a mode-specific bit map.
This routine has no effect when used in a text video mode.
FG_REVMASK displays a reversed image stored as a masking map. This
routine has no effect when used in a text video mode.
FG_SAVE copies an image from the active video page to the same position
on the hidden video page.
FG_SAVEW is the same as fg_save, but the image extremes are specified as
world space coordinates.
FG_SETHPAGE defines the hidden video page (used by fg_restore,
fg_restorew, fg_save, and fg_savew).
FG_TCMASK defines which colors the fg_tcxfer routine will consider
transparent. This routine has no effect when used in a text video mode.
186 Fastgraph User's Guide
FG_TCXFER copies an image from any position on any video page to any
position on any video page, excluding any pixels whose color value is
transparent. This routine has no effect when used in a text video mode.
FG_TRANSFER copies an image from any position on any video page to any
position on any video page. It is Fastgraph's most general image transfer
routine.
Chapter 10
Animation Techniques
188 Fastgraph User's Guide
Overview
Unlike other microcomputers, the IBM PC and PS/2 family of systems do
not have any special graphics hardware or firmware to help in performing
animation. This means that any animation done on these systems must be
implemented entirely through software. This chapter will describe how to do
this using Fastgraph's video page management and image management routines.
The methods described in this chapter are not intended to be all inclusive,
for that would itself fill a separate volume at least as large as this
manual. However, the animation techniques presented here should provide a
basis that you can readily extend to develop more sophisticated uses of
animation. The examples in this chapter are restricted to graphics video
modes.
Simple Animation
The first type of animation we'll examine is called simple animation.
In simple animation, we display an object, erase it, and then display it in a
new position. When we perform this "erase and redisplay" sequence
repetitively, the object moves. This method, however, has two drawbacks.
First, unless the object is rather small, it will flicker because the erasing
and display of the object does not coincide with the refresh rate of the
video display. Second, and perhaps more importantly, anything underneath the
object is not saved as the object moves across it. Despite these
limitations, simple animation is sometimes useful, and it is a good place to
begin our discussion of animation techniques.
Example 10-1 moves a small bright green rectangle (magenta in CGA) from
left to right across the screen in any 320 by 200 color graphics mode. The
program moves the rectangle, 20 pixels wide and 10 pixels high, using a for
loop. This loop first uses the fg_clprect routine to display the rectangle,
then uses the fg_waitfor routine to leave the object on the screen
momentarily, and finally uses fg_clprect again to erase the rectangle by
redisplaying it in the original background color (the fg_waitfor routine is
described in Chapter 14). We use fg_clprect rather than fg_rect because the
first few and last few loop iterations result in at least part of the
rectangle being off the screen. Each successive loop iteration displays the
rectangle five pixels to the right of its previous position.
Example 10-1.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int new_mode, old_mode;
int x;
/* initialize the video environment */
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
Chapter 10: Animation Techniques 189
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
/* move the object across the screen */
for (x = -20; x < 320; x+=5) {
fg_setcolor(10);
fg_clprect(x,x+19,95,104);
fg_waitfor(1);
fg_setcolor(0);
fg_clprect(x,x+19,95,104);
}
/* restore the original video mode and return to DOS */
fg_setmode(old_mode);
fg_reset();
}
Example 10-2 is the same as example 10-1, but it shows what happens when
we move the rectangle across an existing background (in this case, the
background is solid white). If you run this program, you'll see that the
rectangle leaves a trail of color 0 behind it. While this might be
occasionally useful, it demonstrates that simple animation is destructive
because it does not preserve the background. In this example, if we changed
the second call to fg_setcolor within the for loop so revert to color 15
instead of color 0, the background would be restored. In general, though, it
may not be this easy to replace the background, so we must rely on some other
method for preserving it.
Example 10-2.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int new_mode, old_mode;
int x;
/* initialize the video environment */
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
190 Fastgraph User's Guide
/* draw some type of background */
fg_setcolor(15);
fg_rect(0,319,0,199);
/* move the object across the screen */
for (x = -20; x < 320; x+=5) {
fg_setcolor(10);
fg_clprect(x,x+19,95,104);
fg_waitfor(1);
fg_setcolor(0);
fg_clprect(x,x+19,95,104);
}
/* restore the original video mode and return to DOS */
fg_setmode(old_mode);
fg_reset();
}
To summarize, we see that simple animation is easy to implement, but it
is destructive and typically causes the animated object to flicker. For
these reasons, it is not used too frequently.
XOR Animation
"Exclusive or" animation, or XOR animation for short, is an interesting
extension of simple animation and is most useful when animating a single-
color object against a single-color background. Like simple animation, it
uses the "erase and redisplay" technique to move an object, but it does this
differently. Instead of erasing the object by displaying it in the
background color, XOR animation does so by displaying it in the same color
using an exclusive or, or XOR, operation. This method relies on a specific
property of the exclusive or operator:
(object XOR background) XOR object = background
In other words, if you XOR something twice in the same position, the result
is the same as the original image in that position.
Example 10-3 demonstrates XOR animation. This program is similar to
example 10-2, but it only runs in the 320 by 200 EGA graphics mode (mode 13).
After establishing the video mode, it uses the Fastgraph routine fg_setfunc
to select XOR mode. This causes any subsequent graphics output to be XORed
with the contents of video memory instead of just replacing it. The
fg_setfunc routine is described further in Chapter 15.
The other differences between examples 10-3 and 10-2 are that the call
to fg_setcolor has been moved outside the for loop, and that fg_setcolor
takes a different value. Since the existing background is bright white
(color 15), we can't just use color 10 if we want to display a bright green
object. The desired value is that which when XORed with color 15 produces
color 10; the easiest way to obtain this value is to XOR these two numbers.
The call to fg_setcolor can be moved outside the loop because we display the
object using the same color index throughout.
Chapter 10: Animation Techniques 191
Example 10-3.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int old_mode;
int x;
/* initialize the video environment */
if (fg_testmode(13,1) == 0) {
printf("This program requires EGA.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(13);
fg_setfunc(3);
/* draw some type of background */
fg_setcolor(15);
fg_rect(0,319,0,199);
/* move the object across the screen */
fg_setcolor(10^15);
for (x = -20; x < 320; x+=5) {
fg_clprect(x,x+19,95,104);
fg_waitfor(1);
fg_clprect(x,x+19,95,104);
}
/* restore the original video mode and return to DOS */
fg_setmode(old_mode);
fg_reset();
}
Fastgraph only supports the XOR pixel operation in the native EGA and
VGA graphics video modes (modes 13 through 18). Thus, you cannot use XOR
animation in CGA, Tandy/PCjr, Hercules, or MCGA graphics modes.
While XOR animation is non-destructive (that is, it restores the
original background), it still suffers from the flickering encountered in
simple animation. In spite of this, it may be useful when animating a
single-color object against a single-color background.
192 Fastgraph User's Guide
Static Frame Animation
Static frame animation uses a different strategy than simple animation
or XOR animation. The general scheme of this method is to create the entire
animation sequence off-screen and then successively display each item, or
frame, in this sequence on one position of the visual video page. This
results in a visually appealing animation that is non-destructive and does
not include the flickering associated with simple animation and XOR
animation. Static frame animation requires the visual video page and one or
more additional pages to implement. The number of pages needed depends on
the number of frames and the size of each frame.
Example 10-4 runs in any 320 by 200 color graphics video mode and
illustrates a simple use of static frame animation. The program displays an
animation sequence containing 12 frames; it displays this sequence three
times. The animation sequence consists of a bright green rectangle (magenta
in CGA) moving from left to right across the center of the frame. Each frame
is 96 pixels wide and 50 pixels high. The 12 frames are set up on an off-
screen video page as shown below.
0 95 96 191 192 287
0
frame 1 frame 2 frame 3
49
50
frame 4 frame 5 frame 6
99
100
frame 7 frame 8 frame 9
149
150
frame 10 frame 11 frame 12
199
Example 10-4 first establishes the video mode and allocates the
additional video page (needed if using a video mode in which page 1 is a
virtual video page). The program then generates the background for frame 1;
the background is a blue rectangle (cyan in CGA) with a white ellipse
centered on it. After the call to fg_ellipse, the first frame is ready.
The next step is to create the remaining 11 frames. In frame 2, the
right half of the 20-pixel wide rectangle will enter the left edge of the
frame. In frame 3, the rectangle will be ten pixels farther right, or
aligned against the left edge of the frame. In frames 4 through 12, the
rectangle will be ten pixels farther right in each frame, so by frame 12 only
the left half of the rectangle appears on the right edge of the frame. The
first for loop in the program builds frames 2 through 12 by copying the
background from frame 1 and then displaying the rectangle (that is, the
animated object) in the proper position for that frame.
The second for loop performs the animation sequence. To display the 12-
frame sequence three times, it must perform 36 iterations. The loop simply
Chapter 10: Animation Techniques 193
copies each frame from the proper position on video page 1 to the middle of
the visual video page. Note how the fg_waitfor routine is used to pause
momentarily between each frame.
Example 10-4.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
#define VISUAL 0
#define HIDDEN 1
int xmin[] = { 0, 96,192, 0, 96,192, 0, 96,192, 0, 96,192};
int ymax[] = { 49, 49, 49, 99, 99, 99,149,149,149,199,199,199};
void main()
{
int new_mode, old_mode;
int frame, offset;
int i, x, y;
/* initialize the video environment */
new_mode = fg_bestmode(320,200,2);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_allocate(HIDDEN);
/* draw the background in the upper left corner */
fg_setpage(HIDDEN);
fg_setcolor(1);
fg_rect(0,95,0,49);
fg_setcolor(15);
fg_move(48,25);
fg_ellipse(20,20);
/* display the animated object against each background */
fg_setcolor(10);
offset = -10;
for (i = 1; i < 12; i++) {
x = xmin[i];
y = ymax[i];
fg_transfer(0,95,0,49,x,y,HIDDEN,HIDDEN);
fg_setclip(x,x+95,0,199);
fg_clprect(x+offset,x+offset+19,y-29,y-20);
offset += 10;
}
194 Fastgraph User's Guide
/* slide the object across the background three times */
for (i = 0; i < 36; i++) {
frame = i % 12;
x = xmin[frame];
y = ymax[frame];
fg_transfer(x,x+95,y-49,y,112,124,HIDDEN,VISUAL);
fg_waitfor(2);
}
/* restore the original video mode and return to DOS */
fg_freepage(HIDDEN);
fg_setmode(old_mode);
fg_reset();
}
Dynamic Frame Animation
Dynamic frame animation is similar to static frame animation, but all
the animation frames are built as needed during the animation sequence
instead of in advance. When using this method, you must first store a copy
of the background on an off-screen video page. Then, to build a frame,
create another copy (called the workspace) of the background elsewhere on the
off-screen page (or even to a different off-screen page) and display the
object on that copy. Finally, transfer the workspace to the visual page.
Like static frame animation, this method produces a non-destructive, flicker-
free animation sequence.
Example 10-5 is functionally identical to example 10-4, but it uses
dynamic rather than static frame animation. As before, the program builds
the background in the upper left corner of video page 1, but it then uses
fg_transfer to copy it to the center of the visual video page. The for loop
builds each frame as it is needed and also copies it to the center of the
visual page. Again, fg_waitfor creates the necessary pause between frames.
Example 10-5.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
#define VISUAL 0
#define HIDDEN 1
void main()
{
int new_mode, old_mode;
int frame, offset;
int i;
/* initialize the video environment */
new_mode = fg_bestmode(320,200,2);
if (new_mode < 0 || new_mode == 12) {
Chapter 10: Animation Techniques 195
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_allocate(HIDDEN);
/* draw the background in the upper left corner */
fg_setpage(HIDDEN);
fg_setcolor(1);
fg_rect(0,95,0,49);
fg_setcolor(15);
fg_move(48,25);
fg_ellipse(20,20);
/* copy it to the center of the visual page */
fg_transfer(0,95,0,49,112,124,HIDDEN,VISUAL);
/* slide the object across the background three times */
fg_setcolor(10);
for (i = 0; i < 36; i++) {
frame = i % 12;
offset = 10 * frame - 10;
fg_transfer(0,95,20,29,112,105,HIDDEN,HIDDEN);
fg_rect(112+offset,131+offset,96,105);
fg_transfer(112,207,96,105,112,105,HIDDEN,VISUAL);
fg_waitfor(2);
}
/* restore the original video mode and return to DOS */
fg_freepage(HIDDEN);
fg_setmode(old_mode);
fg_reset();
}
Two items in example 10-5 merit further discussion. First, we have
chosen our workspace on page 1 so it uses the same screen space coordinates
as the image area on the visual page. This is not necessary unless you are
using the fg_restore routine instead of fg_transfer. Second, the program can
use the faster fg_rect routine in place of fg_clprect. It can do this
because even though the object will extend beyond the workspace limits, we
only transfer the workspace itself. However, for this to function properly,
the workspace's horizontal limits must fall on byte boundaries.
Note too that we do not need to transfer the entire frame during the
animation sequence. In example 10-5, we know the vertical extremes of the
moving image are y=96 and y=105, so we only transfer 10 rows instead of the
entire frame. We could similarly compute the x extremes for each frame and
only transfer the necessary portion. Recall, however, that fg_transfer
extends the horizontal coordinates to byte boundaries, so we may copy a few
extra pixels as well. This may or may not affect the animation sequence.
196 Fastgraph User's Guide
Again, the problem is eliminated if you align your workspace on byte
boundaries.
When we use dynamic frame animation, it is easy to change the number of
frames in the animation sequence. Suppose we wish to produce a smoother
animation by increasing the number of frames from 12 to 24. This means the
object will move in increments of five pixels instead of ten. The only
changes needed are to double the number of loop iterations, modify the
calculations for the frame number and offset values as shown below, and
reduce the fg_waitfor pause from 2 to 1.
frame = i % 24;
offset = 5 * frame - 10;
Compare this to all the changes that would be necessary if we were using
static frame animation.
Page Flipping
Page flipping is a variation of frame animation in which you construct
images on off-screen video pages and then repetitively make those pages the
visual page. We can further divide the page flipping technique into static
and dynamic variants, as we did with frame animation.
In static page flipping, we construct the entire animation sequence in
advance, with one frame per video page. Once this is done, we can display
each frame by using the fg_setvpage routine to switch instantly from one
video page to another. Although this produces a smooth, flicker-free
animation, we cannot carry the sequence very far before running out of video
pages (and hence animation frames).
In dynamic page flipping, we construct each animation frame when it is
needed. As in static page flipping, we construct each frame on a separate
video page. However, as example 10-6 demonstrates, we only need three video
pages to produce the animation sequence, regardless of the number of frames
in the sequence. Two of the three video pages will alternate as the visual
page, while the remaining video page keeps a copy of the background.
Example 10-6, which performs an animation sequence similar to examples
10-4 and 10-5, illustrates dynamic frame animation in the 320 by 200 EGA
graphics video mode (mode 13). The program begins by displaying the
background on video page 2. Video pages 0 and 1 will alternate as the visual
page; the page that is not the visual page is called the hidden page. We
start with page 0 as the visual page, and hence page 1 as the hidden page.
To build each frame, the program uses fg_transfer to copy the background from
page 2 to the hidden page and then uses fg_clprect to display the animated
object at the correct position on the hidden page. After this, it displays
the next frame by using fg_setvpage to make the hidden page the visual page.
Before beginning the next iteration, the program toggles the hidden page
number in preparation for the next frame.
Example 10-6.
#include <fastgraf.h>
#include <stdio.h>
Chapter 10: Animation Techniques 197
#include <stdlib.h>
void main(void);
void main()
{
int old_mode;
int hidden;
int x;
/* initialize the video environment */
if (testmode(fg_13,3) == 0) {
printf("This program requires EGA.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(13);
/* draw the background on page two */
fg_setpage(2);
fg_setcolor(1);
fg_rect(0,319,0,199);
fg_setcolor(15);
fg_move(160,100);
fg_ellipse(20,20);
/* slide the object across the screen */
hidden = 1;
setcolor(10);
for (x = -10; x < 320; x+=4) {
fg_setpage(hidden);
fg_transfer(0,319,0,199,0,199,2,hidden);
fg_clprect(x,x+19,96,105);
fg_setvpage(hidden);
hidden = 1 - hidden;
fg_waitfor(1);
}
/* restore the original video mode and return to DOS */
fg_setmode(old_mode);
fg_reset();
}
A problem with either page flipping technique arises if we use virtual
video pages. Page flipping relies on the fact that changing the visual page
number occurs instantly, which is exactly what happens when we use physical
video pages. However, such is not the case with virtual or logical pages
because Fastgraph must copy the entire page contents into video memory.
While this occurs quite rapidly, it is not instantaneous, and its effects are
immediately apparent on the animation.
198 Fastgraph User's Guide
Summary of Animation Techniques
This chapter has presented five animation techniques: simple animation,
XOR animation, static frame animation, dynamic frame animation, and page
flipping. The following table summarizes their behavior.
technique destructive? flicker-free?
simple yes no
XOR no no
static frame no yes
dynamic frame no yes
page flipping no yes
Simple animation and XOR animation are elementary techniques that are seldom
used once you master frame animation and page flipping.
As stated at the beginning of this chapter, the simple examples
presented here serve as the basis for understanding the mechanics of the
animation techniques we have discussed. In "real world" programs, you'll
typically want to display an image using the fg_drwimage or fg_drawmap
routines instead using rudimentary images such as the rectangles in our
examples. A helpful rule is to use pixel run maps (displayed by fg_dispfile,
fg_display, or fg_displayp) for both backgrounds and moving objects, and then
use fg_getimage or fg_getmap to retrieve the moving objects as bit-mapped
images for later display. Of course, it is desirable to do this "behind the
scenes" work on video pages other than the visual page.
Chapter 11
Special Effects
200 Fastgraph User's Guide
Overview
This chapter will discuss the Fastgraph routines that help produce
special visual effects. These include the ability to dissolve the screen
contents in small increments, scroll areas of the screen, and change the
physical origin of the screen. The accompanying example programs illustrate
how to use these routines to produce some interesting effects.
Screen Dissolving
Screen dissolving is the process of replacing the entire screen contents
in random small increments instead of all at once. Fastgraph includes two
routines, fg_fadeout and fg_fadein, for this purpose. The fg_fadeout routine
incrementally replaces the visual page contents with pixels of the current
color, while fg_fadein incrementally replaces the visual page contents with
the hidden page contents (that is, the page defined in the most recent call
to fg_sethpage). Both routines accept an integer argument that defines the
delay between each incremental replacement. A value of zero means to perform
the replacement as quickly as possible, while 1 is slightly slower, 2 is
slower yet, and so forth. The fg_fadeout and fg_fadein routines have no
effect in text video modes.
Example 11-1 shows how to use the fg_fadeout routine. The program,
which runs in any graphics video mode, first fills the screen with a
rectangle of color 2. After waiting for a keystroke, the program
incrementally replaces the screen contents with pixels of color 15 (the
current color index when fg_fadeout is called). After another keystroke, the
program exits gracefully.
Example 11-1.
#include <fastgraf.h>
void main(void);
void main()
{
int old_mode;
old_mode = fg_getmode();
fg_setmode(fg_automode());
fg_setcolor(2);
fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
fg_waitkey();
fg_setcolor(15);
fg_fadeout(0);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Example 11-2 shows how to use the fg_fadein routine in any 320 by 200
color graphics video mode. The program first fills the screen with a
Chapter 11: Special Effects 201
rectangle of color 2 and then fills video page 1 with a rectangle of color 1.
After waiting for a keystroke, the program incrementally transfers the
contents of page 1 to the visual page. After the call to fg_fadein, both
page 0 (the visual page) and page 1 (the hidden page) will contain rectangles
of color 1 that fill the entire video page. Finally, the program waits for
another keystroke before returning to DOS.
Example 11-2.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int new_mode, old_mode;
new_mode = fg_bestmode(320,200,2);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_allocate(1);
fg_sethpage(1);
fg_setcolor(2);
fg_rect(0,319,0,199);
fg_setpage(1);
fg_setcolor(1);
fg_rect(0,319,0,199);
fg_waitkey();
fg_fadein(0);
fg_waitkey();
fg_freepage(1);
fg_setmode(old_mode);
fg_reset();
}
You also can produce some appealing visual effects by replacing the
screen contents in a non-random fashion using the fg_restore or fg_transfer
routines. For example, you could copy the hidden page contents to the visual
page with a through a series of concentric rectangular areas, each slightly
larger than the previous, until the entire screen is copied. Another
interesting effect is to start around the screen perimeter and proceed toward
the screen center, thus producing a "snake-like" effect. Experimenting with
such techniques may reveal other effects that suit your application.
202 Fastgraph User's Guide
Scrolling
Another useful effect is scrolling, and Fastgraph provides a routine
that performs vertical scrolling within a given region of the active video
page. The fg_scroll routine scrolls a region defined in screen space or
character space. It can scroll up or down and offers two types of scrolling:
circular and end-off. In circular scrolling, rows that scroll off one edge
of the defined region appear at its opposite edge. In end-off scrolling,
such rows are simply wind up above or below the scrolling area. The
following diagrams illustrate the two types of scrolling.
end-off scrolling circular scrolling
before after before after
C B
A A
A A
B B
In these diagrams, the area bounded by the double lines is the scrolling
region, as specified in the call to fg_scroll. Also, the scrolling direction
is assumed to be down (that is, toward the bottom of the screen), and the
number of rows to scroll is the height of the area designated B. The number
of rows to scroll is often called the scrolling increment.
For the end-off scrolling example, the scrolling operation transfers
region A downward so part of it is copied into area B. The area C (which is
the same size as area B) at the top of the scrolling region is filled with
pixels of the current color index (as defined in the most recent call to
fg_setcolor), and the original contents of area B are lost. The circular
scrolling example also copies region A downward into the original area B.
Unlike end-off scrolling, however, circular scrolling preserves the area B by
copying it to the opposite edge of the scrolling region.
The fg_scroll routine takes six arguments. The first four define the
scrolling region in the order minimum x coordinate, maximum x coordinate,
minimum y coordinate, and maximum y coordinate. In graphics video modes, the
x coordinates are extended by byte boundaries (see page 174) if needed. The
fifth argument is the scrolling increment. It specifies the number of rows
to scroll. If it is positive, the scrolling direction is toward the bottom
of the screen; if it is negative, the scrolling direction is toward the top
of the screen. The sixth and final argument specifies the scroll type. If
this value is zero, the scroll will be circular; if it is any other value,
the scroll will be end-off. If the scroll type is circular, Fastgraph will
use the hidden page (as defined in the most recent call to fg_sethpage) as a
workspace (more specifically, the area bounded by the scrolling region
extremes on the hidden page will be used). We'll now present three example
programs that use the fg_scroll routine.
Example 11-3 runs in any 320 by 200 graphics video mode. The program
displays two lines of text ("line one" and "line two") in the upper left
corner of the screen against a white background. It then uses the fg_scroll
routine to move the second line down four pixel rows using an end-off scroll.
After waiting for a keystroke, the program again uses fg_scroll to move the
Chapter 11: Special Effects 203
text back to its original position. Note especially how the fg_setcolor
routine appears before the first call to fg_scroll to replace the "scrolled
off" rows with pixels of color 15, thus preserving the white background.
Example 11-3.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int new_mode, old_mode;
new_mode = fg_bestmode(320,200,1);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
fg_setcolor(15);
fg_rect(0,319,0,199);
fg_setcolor(10);
fg_text("line one",8);
fg_locate(1,0);
fg_text("line two",8);
fg_waitkey();
fg_setcolor(15);
fg_scroll(0,63,8,15,4,1);
fg_waitkey();
fg_scroll(0,63,12,19,-4,1);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Example 11-4 is similar to example 11-3, but it runs in the 80-column
color text mode (mode 3). In text modes, we cannot scroll half a character
row (four pixels) as in example 11-3, so the program scrolls the minimum one
row instead.
Example 11-4.
#include <fastgraf.h>
void main(void);
void main()
{
int old_mode;
204 Fastgraph User's Guide
old_mode = fg_getmode();
fg_setmode(3);
fg_cursor(0);
fg_setcolor(7);
fg_rect(0,79,0,24);
fg_setattr(10,7,0);
fg_text("line one",8);
fg_locate(1,0);
fg_text("line two",8);
fg_waitkey();
fg_setcolor(7);
fg_scroll(0,7,1,1,1,1);
fg_waitkey();
fg_scroll(0,7,2,2,-1,1);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Example 11-5, the final scrolling example, demonstrates a circular
scroll. The program runs in any 320 by 200 color graphics video mode; note
the use of video page 1 for the workspace required when the fg_scroll routine
performs a circular scroll. The program first fills the screen with a light
blue rectangle (cyan in CGA), displays a smaller white rectangle in the
center of the screen, and then uses fg_move, fg_draw, and fg_paint to display
a light green star (magenta in CGA) within the white rectangle. The program
executes a while loop to scroll the star upward in four pixel increments.
Because the scroll is circular, rows of the star that "scroll off" the top
edge of the white rectangle (whose height is the same as the scrolling
region) reappear at its bottom edge. The use of fg_waitfor within the loop
simply slows down the scroll. The scrolling continues until any key is
pressed.
Example 11-5.
#include <conio.h>
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int new_mode, old_mode;
new_mode = fg_bestmode(320,200,2);
if (new_mode < 0 || new_mode == 12) {
printf("This program requires a 320 ");
printf("x 200 color graphics mode.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(new_mode);
Chapter 11: Special Effects 205
fg_allocate(1);
fg_sethpage(1);
fg_setcolor(9);
fg_rect(0,319,0,199);
fg_setcolor(15);
fg_rect(132,188,50,150);
fg_setcolor(10);
fg_move(160,67);
fg_draw(175,107);
fg_draw(140,82);
fg_draw(180,82);
fg_draw(145,107);
fg_draw(160,67);
fg_paint(160,77);
fg_paint(150,87);
fg_paint(160,87);
fg_paint(170,87);
fg_paint(155,97);
fg_paint(165,97);
while (kbhit() == 0) {
fg_waitfor(1);
fg_scroll(136,184,50,150,-4,0);
}
fg_waitkey();
fg_freepage(1);
fg_setmode(old_mode);
fg_reset();
}
Changing the Screen Origin
Fastgraph includes two routines for changing the screen origin. By
changing the screen origin, we simply mean defining the (x,y) coordinate of
the upper left corner of the display area. The fg_pan routine performs this
function in screen space, while the fg_panw routine does in world space.
Neither routine changes the graphics cursor position.
Each of these routines has two arguments that specify the x and y
coordinates of the screen origin. For the fg_pan routine, the arguments are
integer quantities. For the fg_panw routine, they are floating point
quantities.
In the native EGA and VGA graphics modes (modes 13 through 18), you can
set the screen origin to any (x,y) coordinate position (that is, to any
pixel). In the other graphics modes, certain restrictions exist, as imposed
by specific video hardware. These constraints limit the coordinate positions
that can be used as the screen origin. Fastgraph compensates for these
restrictions by reducing the specified x and y coordinates to values that are
acceptable to the current video mode, as shown in the following table.
206 Fastgraph User's Guide
x will be reduced y will be reduced
video mode to a multiple of: to a multiple of:
4, 5 8 2
6 16 2
9 4 4
11 8 4
12 4 2 or 3
19 to 23 4 1
For example, in modes 4 and 5, the x coordinate will be reduced to a multiple
of 8 pixels, and the y coordinate will be reduced to a multiple of 2 pixels.
In the Hercules low resolution mode (mode 12), the y coordinate reduction
depends on whether or not the specified pixel row is scan doubled.
Example 11-6 shows a useful effect that can be made with the fg_pan or
fg_panw routines. This program uses the fg_automode routine to select a
video mode and then draws an unfilled rectangle in color 15 (bright white).
The top and bottom sides of the rectangle are intentionally drawn just
smaller than the physical screen size. After waiting for a keystroke, the
program uses a for loop to make the rectangle jiggle up and down. The
rectangle moves because the fg_pan routine is called inside the loop to
switch the screen origin between the rectangle's upper left corner and the
original origin. Note also the use of the fg_waitfor routine to cause slight
delays after each call to fg_pan. If we didn't use fg_waitfor, the changing
of the origin would occur so rapidly we wouldn't notice the effect. Finally,
the program restores the original video mode and screen attributes before
returning to DOS.
Example 11-6.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
#define DELAY 2
#define JUMP 4
void main()
{
int i;
int old_mode;
old_mode = fg_getmode();
fg_setmode(fg_automode());
fg_setcolor(15);
fg_move(0,JUMP);
fg_draw(fg_getmaxx(),JUMP);
fg_draw(fg_getmaxx(),fg_getmaxy()-JUMP);
fg_draw(0,fg_getmaxy()-JUMP);
fg_draw(0,JUMP);
fg_waitkey();
Chapter 11: Special Effects 207
for (i = 0; i < 6; i++) {
fg_pan(0,JUMP);
fg_waitfor(DELAY);
fg_pan(0,0);
fg_waitfor(DELAY);
}
fg_setmode(old_mode);
fg_reset();
}
The real power of the fg_pan routine becomes clear when it is used with
the fg_resize routine to perform smooth panning. Recall from Chapter 7 that
fg_resize changes the video page dimensions in native EGA graphics modes
(modes 13 through 16), native VGA graphics modes (modes 17 and 18), and the
extended VGA graphics modes (modes 20 through 23). We'll now present an
example that shows how to use these two routines to perform panning in the
low-resolution EGA graphics mode (mode 13). The method it uses also would
work in any mode that supports video page resizing.
Example 11-7 begins by establishing the video mode and then immediately
calls fg_resize to increase the video page size to 640 x 400 pixels. Thus,
the video page is now four times its original size. Following this, the
program fills the page (the entire page, not just what is displayed) with a
bright green rectangle with a white border around it. It then displays the
message "Press arrow keys to pan" as close as possible to the center of the
page.
The main part of the program is a loop that accepts keystrokes and then
calls fg_pan to perform the panning one pixel at a time. When you press any
of the four arrow keys, the program adjusts the x and y coordinates for the
screen origin as directed. For example, pressing the up arrow key scrolls
the screen upward one pixel. Note that when we reach the edge of the video
page, the program prevents further scrolling in that direction. This process
continues until you press the Esc key, at which time the program restores the
original video mode and screen attributes before exiting.
Example 11-7.
#include <fastgraf.h>
void main(void);
void main()
{
unsigned char key, aux;
int old_mode;
int x, y;
old_mode = fg_getmode();
fg_setmode(13);
fg_resize(640,400);
fg_setcolor(2);
fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
fg_setcolor(15);
fg_box(0,fg_getmaxx(),0,fg_getmaxy());
208 Fastgraph User's Guide
fg_locate(24,28);
fg_text("Press arrow keys to pan.",24);
x = 0;
y = 0;
do {
fg_getkey(&key,&aux);
if (aux == 72 && y < 200)
y++;
else if (aux == 75 && x < 320)
x++;
else if (aux == 77 && x > 0)
x--;
else if (aux == 80 && y > 0)
y--;
fg_pan(x,y);
} while (key != 27);
fg_setmode(old_mode);
fg_reset();
}
Summary of Special Effects Routines
This section summarizes the functional descriptions of the Fastgraph
routines presented in this chapter. More detailed information about these
routines, including their arguments and return values, may be found in the
Fastgraph Reference Manual.
FG_FADEIN incrementally replaces the visual page contents with the
hidden page contents. This routine has no effect in text video modes.
FG_FADEOUT incrementally replaces the visual page contents with pixels
of the current color. This routine has no effect in text video modes.
FG_PAN changes the screen origin (the upper left corner of the screen)
to the specified screen space coordinates. This routine has no effect in
text video modes.
FG_PANW is the world space version of the fg_pan routine.
FG_RESIZE changes the dimensions of a video page in EGA and VGA graphics
modes.
FG_SCROLL vertically scrolls a region of the active video page. The
scrolling may be done either up or down, using either an end-off or circular
method. Circular scrolling uses part of the hidden page as a temporary
workspace.
Chapter 12
Input Device Support
210 Fastgraph User's Guide
Overview
The selection of application input devices is an important part of
designing a program for the IBM PC and PS/2 family of systems. The keyboard
and mouse are very popular, and in fact more and more applications,
especially those that use a graphical interface, actually require a mouse to
use the product. Another input device, primarily used in entertainment
software, is the joystick. Although not as popular as the mouse, the
joystick nevertheless can simplify the use of certain applications.
Fastgraph provides support for these three types of input devices, and this
chapter will discuss this in detail.
Keyboard Support
Fastgraph's keyboard support includes routines to read keystrokes, check
the state of certain keys, and set the state of these keys. These routines
are independent of the other parts of Fastgraph and thus do not require that
you call fg_setmode. All keyboard-related routines work in text and graphics
video modes.
The IBM PC and PS/2 keyboards produce two types of character codes --
standard codes and extended codes (extended codes are sometimes called
auxiliary codes). The standard codes correspond to the 128 characters in the
ASCII character set. In general, pressing keys on the main part of the
keyboard, or on the numeric keypad with NumLock turned on, will generate a
standard code. The 128 extended codes are specific to the IBM PC and PS/2
keyboards. Some common keystrokes that produce extended codes are keys on
the numeric keypad with NumLock turned off, the function keys, or pressing
Alt with another key. The following tables show the standard and extended
keyboard codes.
Table of standard keyboard codes
key code key code key code key code
(none) 0 space 32 @ 64 ` 96
Ctrl+A 1 ! 33 A 65 a 97
Ctrl+B 2 " 34 B 66 b 98
Ctrl+C 3 # 35 C 67 c 99
Ctrl+D 4 $ 36 D 68 d 100
Ctrl+E 5 % 37 E 69 e 101
Ctrl+F 6 & 38 F 70 f 102
Ctrl+G 7 ' 39 G 71 g 103
Ctrl+H 8 ( 40 H 72 h 104
Ctrl+I 9 ) 41 I 73 i 105
Ctrl+J 10 * 42 J 74 j 106
Ctrl+K 11 + 43 K 75 k 107
Ctrl+L 12 , 44 L 76 l 108
Ctrl+M 13 - 45 M 77 m 109
Ctrl+N 14 . 46 N 78 n 110
Ctrl+O 15 / 47 O 79 o 111
Ctrl+P 16 0 48 P 80 p 112
Ctrl+Q 17 1 49 Q 81 q 113
Ctrl+R 18 2 50 R 82 r 114
Chapter 12: Input Device Support 211
Ctrl+S 19 3 51 S 83 s 115
Ctrl+T 20 4 52 T 84 t 116
Ctrl+U 21 5 53 U 85 u 117
Ctrl+V 22 6 54 V 86 v 118
Ctrl+W 23 7 55 W 87 w 119
Ctrl+X 24 8 56 X 88 x 120
Ctrl+Y 25 9 57 Y 89 y 121
Ctrl+Z 26 : 58 Z 90 z 122
Ctrl+[ 27 ; 59 [ 91 { 123
Ctrl+\ 28 < 60 \ 92 | 124
Ctrl+] 29 = 61 ] 93 } 125
Ctrl+^ 30 > 62 ^ 94 ~ 126
Ctrl+- 31 ? 63 _ 95 Ctrl+BS 127
Table of extended keyboard codes
code key
3 Ctrl+@
15 Shift+Tab (back tab)
16-25 Alt+Q to Alt+P (top row of letters)
30-38 Alt+A to Alt+L (middle row of letters)
44-50 Alt+Z to Alt+M (bottom row of letters)
59-68 F1 to F10
71 Home
72 up arrow
73 PgUp
75 left arrow
77 right arrow
79 End
80 down arrow
81 PgDn
82 Ins
83 Del
84-93 Shift+F1 to Shift+F10
94-103 Ctrl+F1 to Ctrl+F10
104-113 Alt+F1 to Alt+F10
114 Ctrl+PrtSc
115 Ctrl+left arrow
116 Ctrl+right arrow
117 Ctrl+End
118 Ctrl+PgDn
119 Ctrl+Home
120-131 Alt+1 to Alt+= (top row of keys)
132 Ctrl+PgUp
In addition, four keys generate the same standard codes as other control key
combinations. These keys are:
key same as code
Backspace Ctrl+H 8
Tab Ctrl+I 9
Enter Ctrl+M 13
Escape Ctrl+[ 27
212 Fastgraph User's Guide
The CapsLock, NumLock, and ScrollLock keys do not generate a standard or
extended code when pressed. Instead, they toggle between off and on states.
Reading Keystrokes
When you press a key or key combination, the standard or extended code
representing that keystroke is stored in the ROM BIOS keyboard buffer. This
buffer can hold up to 16 keystrokes and thus provides a type-ahead
capability. Fastgraph includes three routines for reading keystroke
information from the keyboard buffer. The fg_getkey routine reads the next
item in the keyboard buffer if one is available (that is, if a key has been
pressed). If the keyboard buffer is empty (meaning no key has been pressed),
fg_getkey waits for a keystroke and then reports information about it.
Another routine, fg_intkey, reads the next keystroke from the keyboard buffer
if one is available. If the keyboard buffer is empty, fg_intkey immediately
returns and reports this condition. The fg_intkey routine is useful when a
program must continue performing a task until a key is pressed. We've
already seen the third routine, fg_waitkey, which flushes the keyboard buffer
and then waits for another keystroke. Unlike fg_getkey and fg_intkey,
fg_waitkey does not return any keystroke information. It is most useful in
"press any key to continue" situations.
Both the fg_getkey and fg_intkey routines require two one-byte arguments
passed by reference. If the keystroke is represented by a standard keyboard
code, fg_getkey and fg_intkey return its code in the first argument and set
the second argument to zero. Similarly, if the keystroke generates an
extended code, the routines return its code in the second argument and set
the first argument to zero. If the fg_intkey routine detects an empty
keyboard buffer, it sets both arguments to zero.
Example 12-1 is a simple program that uses the fg_getkey routine. It
solicits keystrokes and then displays the two values returned by fg_getkey,
one of which will always be zero. The variable key receives the key's
standard code, while aux receives its extended code. Note that fg_getkey is
the only Fastgraph routine in the program; this can be done because the
keyboard support routines are logically independent from the rest of
Fastgraph. The program returns to DOS when you press the Escape key.
Example 12-1.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
#define ESC 27
void main()
{
unsigned char key, aux;
do {
fg_getkey(&key,&aux);
printf("key = %3d aux = %3d\n",key,aux);
}
while (key != ESC);
}
Chapter 12: Input Device Support 213
Example 12-2 reads keystrokes using the fg_intkey routine at half-second
intervals (18 fg_waitfor units equals one second). As in the previous
example, the program displays the standard and extended codes for each
keystroke. However, example 12-2 will continuously execute the while loop
even if no keystrokes are available, in which case the key and aux values
will both be zero. The program returns to DOS when you press the Escape key.
Example 12-2.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
#define ESC 27
void main()
{
unsigned char key, aux;
do {
fg_waitfor(9);
fg_intkey(&key,&aux);
printf("key = %3d aux = %3d\n",key,aux);
}
while (key != ESC);
}
When you use fg_intkey in a "tight" loop that does little else, you
should force a small delay within the loop by calling fg_waitfor as in
example 12-2. Typically a delay of one or two clock ticks is enough.
Without this delay, the BIOS may not be able to handle all keyboard activity,
and thus some keystrokes may not be available to your program.
Testing and Setting Key States
As mentioned earlier, the CapsLock, NumLock, and ScrollLock keys do not
generate a standard or extended code when pressed but instead toggle between
off and on states. Fastgraph includes routines for checking the state of
these keys, as well as setting the state of the CapsLock and NumLock keys.
The Fastgraph routines fg_capslock, fg_numlock, and fg_scrlock
respectively read the state of the CapsLock, NumLock, and ScrollLock keys.
Each routine has no arguments and returns the key state as its function
value. A return value of 0 means the associated key is in the off state,
while 1 indicates the key is in the on state. If the keyboard does not have
a ScrollLock key, fg_scrlock considers the key off and returns a value of
zero.
Example 12-3 is a simple program that uses the fg_capslock, fg_numlock,
and fg_scrlock routines to print messages describing the current state of
these three keys.
214 Fastgraph User's Guide
Example 12-3.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
void main()
{
if (fg_capslock())
printf("CapsLock is on.\n");
else
printf("CapsLock is off.\n");
if (fg_numlock())
printf("NumLock is on.\n");
else
printf("NumLock is off.\n");
if (fg_scrlock())
printf("ScrollLock is on.\n");
else
printf("ScrollLock is off.\n");
}
You also can set the state of the CapsLock and NumLock keys within a
program. Fastgraph includes two routines, fg_setcaps and fg_setnum, for this
purpose. Each routine requires an integer argument that specifies the new
key state. If the argument value is 0, the key will be turned off; if the
value is 1, the key will be turned on. Example 12-4 uses fg_setcaps and
fg_setnum to turn off CapsLock and NumLock.
Example 12-4.
#include <fastgraf.h>
void main(void);
void main()
{
fg_setcaps(0);
fg_setnum(0);
}
On most keyboards, changing key states with fg_setcaps or fg_setnum also
will change the keyboard state light to reflect the new key state. However,
some older keyboards, especially when used on PC, PC/XT, or Tandy 1000
systems, do not update the state light. This makes the state light
inconsistent with the true key state.
Mouse Support
The mouse is a very popular input and pointing device, especially in
graphically-oriented programs. Fastgraph contains several routines to
support mice. These routines perform such tasks as mouse initialization,
controlling and defining the mouse cursor, and reporting information about
the mouse position and button status.
Chapter 12: Input Device Support 215
The underlying software that controls the mouse is called the mouse
driver. Fastgraph's mouse support routines provide a high-level interface to
this driver. The Microsoft Mouse and its accompanying mouse driver have
become an industry standard, and other manufacturers of mice have also made
their mouse drivers Microsoft compatible. For this reason, the Fastgraph
mouse support routines assume you are using a Microsoft or compatible mouse
driver.
Unfortunately, not all mouse drivers are created equal. That is, some
drivers are not Microsoft compatible, even though they may be advertised as
such. In some cases, these incompatibilities are rather trivial, but others
are significant. For example, early versions of some third party mouse
drivers had real problems in the EGA graphics modes. The Microsoft mouse
driver, the Logitech mouse driver (version 3.2 or above), and the DFI mouse
driver (version 3.00 or above) are known to work well with Fastgraph's mouse
support routines. Any other Microsoft compatible mouse driver also should
work properly.
Initializing the Mouse
There are two steps required to use Fastgraph's mouse support routines
within an application program. First, you must install the mouse driver.
This is done before running the application, typically by entering the
command MOUSE at the DOS command prompt. Second, you must use the Fastgraph
routine fg_mouseini to initialize the mouse within the program.
The fg_mouseini routine has no arguments and returns a "success or
failure" indicator as its function value. If the return value is -1, it
means fg_mouseini could not initialize the mouse (either because the mouse
driver is not installed, or the driver is installed but the mouse is
physically disconnected). The fg_mouseini routine also will return -1 when
used in the extended VGA graphics video modes (modes 20 through 23) because
there is no mouse support available in these video modes. If fg_mouseini
returns a positive integer value, then the mouse initialization was
successful. The value itself indicates the number of buttons (either 2 or 3)
on the mouse. If you don't call fg_mouseini, or if fg_mouseini can't
initialize the mouse, none of Fastgraph's other mouse support routines will
have any effect.(3)
Example 12-5 illustrates how to initialize the mouse. Unlike the
keyboard support routines, Fastgraph's mouse support routines require that
fg_setmode first be called. In this example, we simply pass fg_setmode the
value -1 to initialize Fastgraph for whatever video mode is in effect when we
run the program. The program then calls fg_mouseini and prints a message
indicating whether or not the initialization was successful. If it was, the
message includes the number of buttons on the mouse.
Example 12-5.
#include <fastgraf.h>
____________________
(3) If you use another mouse library or communicate directly with the
mouse driver, you must still call fg_mouseini if your program runs in modes
13 through 18. Otherwise, Fastgraph won't know that your program is using
a mouse and may display graphics incorrectly.
216 Fastgraph User's Guide
#include <stdio.h>
void main(void);
void main()
{
int status;
fg_setmode(-1);
status = fg_mouseini();
if (status < 0)
printf("Mouse not available.\n");
else
printf("%d button mouse found.\n",status);
}
You should be aware that certain mouse drivers do not fully initialize
the mouse when a program changes video modes. This problem most frequently
occurs when you restore the original video mode at the end of a program that
has called fg_mouseini. When changing video modes, you must first make the
mouse cursor invisible (this is described in the next section), change the
video mode, and then call fg_mouseini again to initialize the mouse for the
new video mode.
Controlling the Mouse Cursor
The mouse cursor indicates the current position of the mouse. By
default, the cursor is a small white arrow in graphics modes and a one-
character rectangle in text modes. After you use fg_mouseini to initialize
the mouse, the mouse cursor is invisible. To make it visible, you must use
the fg_mousevis routine. This routine has a single integer argument that
defines the mouse cursor visibility. If it is 0, the mouse cursor will be
invisible; if it is 1, the mouse cursor becomes visible.
If the mouse cursor is in an area of the screen that is being updated,
or if it moves into this area during the update process, you must make the
mouse cursor invisible. Additionally, when performing any video output in
the native EGA and VGA graphics modes (modes 13 through 18), you also must
make the mouse cursor invisible. Instead of checking for these conditions,
it is more convenient and efficient to make the mouse cursor invisible during
all screen updates and then make it visible again when the updating is
finished. Finally, you must make the mouse cursor invisible whenever you
change the visual page number with fg_setvpage.
After you initialize the mouse, the cursor is positioned in the center
of the screen. Moving the mouse of course changes the cursor position, but
you also can position the mouse cursor with the Fastgraph routine
fg_mousemov. This routine has two arguments that specify the new horizontal
and vertical cursor position. The position is expressed in screen space
units for graphics modes, while it is expressed in character cells for text
modes. The fg_mousemov routine moves the cursor whether or not it is
visible.
Sometimes it is useful to restrict the mouse cursor to a specific area
of the screen. The Fastgraph routine fg_mouselim prevents the mouse cursor
from moving outside the specified rectangular area. It requires four
Chapter 12: Input Device Support 217
arguments that specify the minimum horizontal coordinate, maximum horizontal
coordinate, minimum vertical coordinate, and maximum vertical coordinate of
this area. Again, the coordinates are expressed in screen space units for
graphics modes and character cells for text modes.
One of the most important functions of the mouse driver is to translate
the horizontal and vertical mouse movements into a position on the screen.
The mouse reports these movements to the mouse driver in units called mickeys
(one mickey is about 1/200 of an inch). By default, moving the mouse 8
mickeys in the horizontal direction moves the mouse cursor one horizontal
pixel. Similarly, moving the mouse 16 mickeys vertically moves the cursor
one vertical pixel. Fastgraph provides a routine named fg_mousespd that can
change these values, which effectively allows you to control the speed at
which the mouse cursor moves relative to the movement of the mouse itself.
The fg_mousespd routine requires two arguments that define the number of
mickeys required for eight pixels of mouse cursor movement. The first
argument specifies this for the horizontal direction, and the second for the
vertical direction.
Example 12-6, which runs in any graphics mode, demonstrates the
fg_mousevis, fg_mousemov, fg_mouselim, and fg_mousespd routines. The program
first establishes the video mode, initializes the mouse, and fills the screen
with a white rectangle. Next, the program calls fg_mousevis to make the
mouse cursor visible and then calls fg_mouselim to restrict the mouse cursor
to an area one-fourth the size of the screen, centered in the middle of the
screen. At this point you should move the mouse cursor around the screen to
see the effect of fg_mouselim and note the speed at which the cursor moves
relative to the mouse itself. The program continues when you press any key.
The program then uses fg_mousemov to move the mouse cursor to each
corner of the region established by fg_mouselim. The call to fg_waitfor
keeps the cursor in each corner for two seconds, unless you move the mouse.
Note how the program tries to move the mouse cursor to each corner of the
screen, but since doing so would move the cursor outside the defined region
of movement, fg_mousemov just positions the cursor at the nearest point
possible within this region. The last call to fg_mousemov moves the cursor
back to the middle of the screen. After doing this, the program calls
fg_mousespd to change the mouse cursor speed. The values passed to
fg_mousespd (16 and 32) are twice the defaults and therefore make you move
the mouse twice as far as before to move the mouse cursor the same distance.
When you run the program, compare the mouse sensitivity to the original
speed. After a keystroke, the program returns to DOS.
Example 12-6.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int maxx, maxy;
int old_mode;
old_mode = fg_getmode();
218 Fastgraph User's Guide
fg_setmode(fg_automode());
if (fg_mouseini() < 0) {
fg_setmode(old_mode);
fg_reset();
exit(1);
}
maxx = fg_getmaxx();
maxy = fg_getmaxy();
fg_setcolor(15);
fg_rect(0,maxx,0,maxy);
fg_mousevis(1);
fg_mouselim(maxx/4,3*maxx/4,maxy/4,3*maxy/4);
fg_waitkey();
fg_mousemov(0,0);
fg_waitfor(36);
fg_mousemov(maxx,0);
fg_waitfor(36);
fg_mousemov(maxx,maxy);
fg_waitfor(36);
fg_mousemov(0,maxy);
fg_waitfor(36);
fg_mousemov(maxx/2,maxy/2);
fg_mousespd(16,32);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Reporting the Mouse Status
It is obviously important to be able to track the mouse position and
button status. The Fastgraph routines fg_mousepos and fg_mousebut enable you
to do this.
The fg_mousepos routine returns information about the current mouse
cursor position and button status. It requires three integer arguments, all
passed by reference. The first two arguments respectively receive the
horizontal and vertical coordinates of the mouse cursor. These values are
expressed in screen space units for graphics modes and character cells for
text modes. The third argument receives a three-bit mask containing the
button status as indicated below.
bit
number meaning
0 1 if left button pressed, 0 if not
1 1 if right button pressed, 0 if not
2 1 if middle button pressed, 0 if not
Chapter 12: Input Device Support 219
For example, if both the left and right buttons are pressed, the button
status will be set to 3. If the mouse only has two buttons, bit 2 will
always be zero.
Another routine, fg_mousebut, is available for returning the number of
button press or release counts that have occurred since the last check, or
since calling fg_mouseini. Each mouse button maintains its own separate
counters, so fg_mousebut returns this information for a specific button.
Additionally, fg_mousebut returns the horizontal and vertical position of the
mouse cursor at the time the specified button was last pressed or released.
The fg_mousebut routine takes four integer arguments, of which the last
three are passed by reference. The first argument specifies the button of
interest (1 means the left button, 2 is the right button, and 3 is the middle
button). If this value is positive, button press counts will be reported.
If it is negative, release counts will be reported. The second, third, and
fourth arguments respectively receive the press or release count, the
horizontal mouse cursor position at the time of the last press or release,
and the vertical position at that same time. If the press or release count
is zero, the mouse cursor position is returned as (0,0). The coordinate
positions are expressed in screen space units for graphics modes and
character cells for text modes.
Example 12-7 runs in any graphics video mode and illustrates the use of
the fg_mousepos and fg_mousebut routines. The program first establishes the
video mode and then initializes the mouse (the program exits if the
initialization fails). It next fills the entire screen with a white
rectangle and then calls fg_mousevis to make the mouse cursor visible.
The main part of example 12-7 is a while loop that polls the mouse at
three-second intervals (the call fg_waitfor(54) delays the program for three
seconds). Within the loop, the program first uses fg_mousebut to get the
number of times the left mouse button was pressed in the last three seconds.
Following this, the fg_mousepos routine gets the current mouse position. The
program then displays this information in the upper left corner of the
screen; note how fg_mousevis is used to make the cursor invisible during
graphics operations. The program continues until you press the right mouse
button, checked by the call to fg_mousebut at the end of the loop.
Example 12-7.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int old_mode;
int buttons, count;
int x, y;
char string[25];
old_mode = fg_getmode();
fg_setmode(fg_automode());
220 Fastgraph User's Guide
if (fg_mouseini() < 0) {
fg_setmode(old_mode);
fg_reset();
exit(1);
}
fg_setcolor(15);
fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
fg_mousevis(1);
do {
fg_waitfor(54);
fg_mousebut(1,&count,&x,&y);
fg_mousepos(&x,&y,&buttons);
sprintf(string,"X=%3d Y=%3d count=%4d",x,y,count);
fg_mousevis(0);
fg_setcolor(15);
fg_rect(0,fg_xconvert(25),0,fg_yconvert(1));
fg_setcolor(0);
fg_locate(0,0);
fg_text(string,24);
fg_mousevis(1);
fg_mousebut(2,&count,&x,&y);
}
while (count == 0);
fg_setmode(old_mode);
fg_reset();
}
Defining the Mouse Cursor
By default, the mouse cursor is a small white arrow in graphics modes
and a one-character rectangle in text modes. In graphics modes, you can
change the mouse cursor to any 16 by 16 pixel image with the Fastgraph
routine fg_mouseptr (in the CGA four-color graphics modes, the cursor size is
8 by 16 pixels). You cannot change the mouse cursor shape in text modes, but
you can use the Fastgraph routine fg_mousecur to define how it interacts with
existing characters on the screen.
Text Modes
To change the mouse cursor in text modes, you must first define two 16-
bit quantities called the screen mask and cursor mask. The following figure
defines the format of each mask.
bits meaning
0 to 7 ASCII character value
8 to 11 foreground color
12 to 14 background color
15 blink
Chapter 12: Input Device Support 221
Notice how this structure parallels the character and attribute bytes
associated with each character cell. The default screen mask is 77FF hex,
and the default cursor mask is 7700 hex.
When you position the mouse over a specific character cell, the mouse
driver uses the current screen and cursor masks to determine the mouse
cursor's appearance. First, the mouse driver logically ANDs the screen mask
with the existing contents of that character cell. It then XORs that result
with the cursor mask to display the mouse cursor.
For example, consider how the mouse cursor is produced in the 80-column
color text mode (mode 3). Suppose a specific character cell contains the
ASCII character 0 (48 decimal, 30 hex) and an attribute byte that specifies a
white (color 15) foreground on a blue background (color 1) and does not blink
(blink bit 0). The binary structure of the character and its attribute are:
attribute character
0 001 1111 00110000
Now let's see what happens when we apply the screen and cursor masks to the
character and its attribute.
attribute/character 0001 1111 0011 0000 (1F30 hex)
default screen mask 0111 0111 1111 1111 (77FF hex)
-------------------
result of AND 0001 0111 0011 0000 (1730 hex)
default cursor mask 0111 0111 0000 0000 (7700 hex)
-------------------
result of XOR 0110 0000 0011 0000 (6030 hex)
The resulting character (30 hex) is the original character, but the new
attribute (60 hex) represents a black foreground with a brown background and
does not blink. As long as the mouse cursor remains positioned on this
character cell, it would appear black on brown.
When we use the default screen and cursor masks, the mouse cursor will
always display the original character and it will not blink. The cursor
foreground color will be 15-F, where F is the displayed character's
foreground color. Similarly, the cursor background color will be 7-B, where
B is the displayed character's background color. The default masks will
virtually always produce a satisfactory mouse cursor.
It is possible, however, to change the appearance of the mouse cursor in
text modes by using your own screen and cursor masks. The Fastgraph routine
fg_mousecur does just that. It expects two arguments, the first being the
cursor mask and the second the screen mask. Example 12-8 demonstrates the
use of fg_mousecur. The program displays some text and uses the default
mouse cursor. After waiting for a keystroke, the program calls fg_mousecur
to define a new mouse cursor. The new cursor is similar to the default
cursor, but it displays the foreground colors in the opposite intensity as
222 Fastgraph User's Guide
the default cursor. The program then waits for another keystroke before
returning to DOS.
Example 12-8.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int old_mode;
int row;
old_mode = fg_getmode();
fg_setmode(3);
if (fg_mouseini() < 0) {
fg_setmode(old_mode);
fg_reset();
exit(1);
}
fg_setattr(7,0,0);
fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
fg_setattr(12,7,0);
for (row = 0; row < 25; row++) {
fg_locate(row,34);
fg_text("example 12-8",12);
}
fg_mousevis(1);
fg_waitkey();
fg_mousecur(0x7FFF,0x7F00);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
Graphics Modes
Defining the mouse cursor in graphics video modes also requires creating
a screen mask and cursor mask, but as one might expect, the structure of
these masks is vastly different from for text modes. In fact, it closely
resembles the mode-independent bit map format used by the fg_drawmap routine.
Although their structure differs, the way the mouse driver uses the masks is
the same as in the text modes. That is, the driver displays the mouse cursor
by first logically ANDing video memory with the screen mask, and then XORing
that result with the cursor mask.
Let's begin by looking at the masks for the default mouse cursor in
graphics modes. The size of each mask (and hence the mouse cursor) is 16
pixels wide and 16 pixels high. As mentioned earlier, the default cursor is
Chapter 12: Input Device Support 223
a small white arrow with a black outline around it. Here are its screen and
cursor masks expressed as binary values.
screen cursor cursor
mask mask appearance
1001111111111111 0000000000000000 **
1000111111111111 0010000000000000 *x*
1000011111111111 0011000000000000 *xx*
1000001111111111 0011100000000000 *xxx*
1000000111111111 0011110000000000 *xxxx*
1000000011111111 0011111000000000 *xxxxx*
1000000001111111 0011111100000000 *xxxxxx*
1000000000111111 0011111110000000 *xxxxxxx*
1000000000011111 0011111111000000 *xxxxxxxx*
1000000000001111 0011111000000000 *xxxxx*****
1000000011111111 0011011000000000 *xx*xx*
1000100001111111 0010001100000000 *x* *xx*
1001100001111111 0000001100000000 ** *xx*
1111110000111111 0000000110000000 *xx*
1111110000111111 0000000110000000 *xx*
1111111000111111 0000000000000000 ***
The mouse driver first ANDs the screen mask with video memory at the
mouse cursor position. This means the screen mask 1 bits leave video memory
intact, while the 0 bits change the corresponding pixels to black. Next, the
mouse driver XORs the result with the cursor mask. This time the cursor mask
0 bits leave video memory unchanged, while the 1 bits change the
corresponding pixels to white. This produces a mouse cursor as shown above
on the right, where a dot ( ) represents an unchanged pixel, an asterisk (*)
a black pixel, and an x a white pixel. The following table summarizes the
cursor appearance for all possible combinations of mask bits.
screen mask bit cursor mask bit resulting cursor pixel
0 0 black
0 1 white
1 0 unchanged
1 1 inverted
The color of an "inverted" pixel is n-k, where n is the maximum color
number in the current video mode, and k is the color of the pixel being
replaced. Also, "black" and "white" pixels are not necessarily these colors
in 16-color and 256-color modes. More correctly, "black" pixels are
displayed in the color assigned to palette 0, and "white" pixels are the
displayed in the color assigned to palette 15. If you're using the CGA color
modes, "black" pixels are displayed in the background color, and "white"
pixels appear in color 3 (whose actual color is determined by the selected
CGA palette).
With an understanding of the way the default mouse cursor works in
graphics modes, we're now ready to define our own mouse cursor. Shown below
are the screen mask, cursor mask, and resulting appearance for a solid plus-
shaped cursor. The hexadecimal equivalents of the binary mask values are
also given.
224 Fastgraph User's Guide
----- screen mask ---- ----- cursor mask ----
cursor
binary hex binary hex appearance
1110000000111111 E03F 0000000000000000 0000 ...*******......
1110000000111111 E03F 0000111110000000 0F80 ...*xxxxx*......
1110000000111111 E03F 0000111110000000 0F80 ...*xxxxx*......
0000000000000111 0007 0000111110000000 0F80 ****xxxxx****...
0000000000000111 0007 0111111111110000 7FF0 *xxxxxxxxxxx*...
0000000000000111 0007 0111111111110000 7FF0 *xxxxxxxxxxx*...
0000000000000111 0007 0111111111110000 7FF0 *xxxxxxxxxxx*...
0000000000000111 0007 0111111111110000 7FF0 *xxxxxxxxxxx*...
0000000000000111 0007 0111111111110000 7FF0 *xxxxxxxxxxx*...
0000000000000111 0007 0000111110000000 0F80 ****xxxxx****...
1110000000111111 E03F 0000111110000000 0F80 ...*xxxxx*......
1110000000111111 E03F 0000111110000000 0F80 ...*xxxxx*......
1110000000111111 E03F 0000000000000000 0000 ...*******......
1111111111111111 FFFF 0000000000000000 0000 ................
1111111111111111 FFFF 0000000000000000 0000 ................
1111111111111111 FFFF 0000000000000000 0000 ................
If we wanted to make the mouse cursor hollow rather than solid, the masks and
resulting cursor appearance would look like this.
----- screen mask ---- ----- cursor mask ----
cursor
binary hex binary hex appearance
1110000000111111 E03F 0000000000000000 0000 ...*******......
1110111110111111 EFBF 0000000000000000 0000 ...*.....*......
1110111110111111 EFBF 0000000000000000 0000 ...*.....*......
0000111110000111 0F87 0000000000000000 0000 ****.....****...
0111111111110111 7FF7 0000000000000000 0000 *...........*...
0111111111110111 7FF7 0000000000000000 0000 *...........*...
0111111111110111 7FF7 0000001000000000 0200 *.....x.....*...
0111111111110111 7FF7 0000000000000000 0000 *...........*...
0111111111110111 7FF7 0000000000000000 0000 *...........*...
0000111110000111 0F87 0000000000000000 0000 ****.....****...
1110111110111111 EFBF 0000000000000000 0000 ...*.....*......
1110111110111111 EFBF 0000000000000000 0000 ...*.....*......
1110000000111111 E03F 0000000000000000 0000 ...*******......
1111111111111111 FFFF 0000000000000000 0000 ................
1111111111111111 FFFF 0000000000000000 0000 ................
1111111111111111 FFFF 0000000000000000 0000 ................
Note that the center bit defined in the cursor mask causes the corresponding
pixel in video memory to be inverted.
There is one more item needed to define a graphics mode mouse cursor
completely. That item is the hot spot, or the actual screen position used or
reported by the mouse driver. For the plus-shaped cursors just constructed,
it would be sensible to define the hot spot in the center of the plus. The
hot spot is specified relative to the upper left corner of the cursor, so its
position within the cursor would be (6,6) -- that is, six pixels to the right
and six pixels below the upper left corner. You can specify the hot spot
offsets using negative values or values above 15 to position it outside the
mouse cursor matrix if desired.
Chapter 12: Input Device Support 225
The Fastgraph routine fg_mouseptr defines a mouse cursor in graphics
modes. The first of its three arguments is a 32-element integer array,
passed by reference. The array's first 16 elements contain the screen mask,
and its second 16 elements contain the cursor mask. The remaining two
arguments respectively specify the horizontal and vertical offsets for the
hot spot. The fg_mouseptr routine has no effect in a text video mode.
Example 12-9 is similar to example 12-8. It shows how to define a
graphics mode mouse cursor using fg_mouseptr. The values stored in the solid
and hollow arrays define the screen and cursor masks for the solid and hollow
plus-shaped mouse cursors discussed earlier. After making the mouse cursor
visible, the program uses the default mouse cursor until a key is pressed.
Following this, it changes to the solid cursor. After another keystroke, the
program changes to the hollow cursor. When you run example 12-9, compare the
differences among the three mouse cursors.
Example 12-9.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
int solid[] = {0xE03F,0xE03F,0xE03F,0x0007,0x0007,0x0007,0x0007,0x0007,
0x0007,0x0007,0xE03F,0xE03F,0xE03F,0xFFFF,0xFFFF,0xFFFF,
0x0000,0x0F80,0x0F80,0x0F80,0x7FF0,0x7FF0,0x7FF0,0x7FF0,
0x7FF0,0x0F80,0x0F80,0x0F80,0x0000,0x0000,0x0000,0x0000};
int hollow[] = {0xE03F,0xEFBF,0xEFBF,0x0F87,0x7FF7,0x7FF7,0x7FF7,0x7FF7,
0x7FF7,0x0F87,0xEFBF,0xEFBF,0xE03F,0xFFFF,0xFFFF,0xFFFF,
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0200,0x0000,
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000};
void main()
{
int old_mode;
int column, row, last_row;
old_mode = fg_getmode();
fg_setmode(fg_automode());
if (fg_mouseini() < 0) {
fg_setmode(old_mode);
fg_reset();
exit(1);
}
fg_setcolor(15);
fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
fg_setcolor(12);
column = fg_xalpha(fg_getmaxx()/2) - 6;
last_row = fg_yalpha(fg_getmaxy()) + 1;
for (row = 0; row < last_row; row++) {
fg_locate(row,column);
fg_text("example 12-9",12);
226 Fastgraph User's Guide
}
fg_mousevis(1);
fg_waitkey();
fg_mouseptr(solid,6,6);
fg_waitkey();
fg_mouseptr(hollow,6,6);
fg_waitkey();
fg_setmode(old_mode);
fg_reset();
}
CGA Considerations
The mouse driver treats the screen and cursor masks differently in the
CGA four-color graphics modes (modes 4 and 5) than in the other graphics
modes. In the CGA modes, each pair of mask bits corresponds to one pixel.
This means the masks more closely resemble the mode-specific format used by
fg_drwimage instead of the mode-independent format of fg_drawmap.
Fastgraph uses a different default mouse cursor for modes 4 and 5. Its
screen and cursor masks, as well as the resulting cursor appearance, are
shown in the following diagram.
screen cursor cursor
mask mask appearance
0000111111111111 0000000000000000 **
0000001111111111 0011000000000000 ***
0000000011111111 0011110000000000 ****
0000000000111111 0011111100000000 *****
0000000000001111 0011111111000000 ******
0000000000000011 0011111111110000 *******
0000000000000011 0011111100000000 *******
0000000000111111 0011111110000000 *****
0000000000001111 0011000011000000 ******
0000110000001111 0000000011000000 ** ***
1111111100000011 0000000000110000 ***
1111111100000011 0010000000110000 ***
1111111111000011 0000000000000000 **
1111111111111111 0000000000000000
1111111111111111 0000000000000000
1111111111111111 0000000000000000
As you can see, the resulting mouse cursor is eight pixels wide instead of
16.
Another important point concerning mouse cursors in modes 4 and 5 is the
chance of pixel bleeding, or the changing of colors within the mouse cursor
as it moves horizontally. Bleeding will occur if you use the bit pairs 01 or
10 in either mask to represent a pixel. In the default masks for modes 4 and
5, note that only the binary values 00 and 11 appear as bit pairs. Keep this
in mind if you create your own masks in these video modes.
Chapter 12: Input Device Support 227
Joystick Support
The third type of input device supported by Fastgraph is the joystick.
Although joysticks are not as popular as mice, they are often preferable when
a user's reactions are critical, such as in an arcade-style game. Fastgraph
includes routines for initializing a joystick, reading a joystick's position
or button status, and making a joystick behave analogously to the keyboard.
These routines are independent of the rest of Fastgraph and thus do not
require that you first call the fg_setmode routine.
Joysticks are connected to a system through a game port. The PCjr and
Tandy 1000 systems come equipped with two game ports, and hence support two
joysticks. On other systems in the IBM family, you can install a game port
card that contains either one or two game ports. If the card only has one
game port, you can use a splitter cable to fork two joysticks into the port.
Initializing Joysticks
Before you can use any of Fastgraph's joystick support routines with a
specific joystick, you must initialize that joystick. The fg_initjoy routine
performs this task. This routine requires a single integer argument that
specifies which joystick to initialize, either 1 or 2. If successful,
fg_initjoy returns 0 as the function value. If the machine has no game port,
or if the requested joystick is not connected to the game port, fg_initjoy
returns -1. When you use fg_initjoy, the joystick being initialized must be
centered (that is, the stick itself must not be tilted in either direction).
Example 12-10 uses the fg_initjoy routine to try to initialize both
joysticks. For each joystick, the program prints a message stating whether
or not the initialization was successful.
Example 12-10.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
void main()
{
if (fg_initjoy(1) < 0)
printf("Joystick 1 not available.\n");
else
printf("Joystick 1 found.\n");
if (fg_initjoy(2) < 0)
printf("Joystick 2 not available.\n");
else
printf("Joystick 2 found.\n");
}
228 Fastgraph User's Guide
Reporting Joystick Status
Each joystick can report three items: its horizontal position, its
vertical position, and the button status. Fastgraph includes routines for
obtaining each of these quantities.
The fg_getxjoy and fg_getyjoy routines respectively return the
horizontal and vertical position of the indicated joystick. Both routines
require a single integer argument, whose value is either 1 or 2, to identify
the joystick. The requested position is returned as the function value.
Horizontal coordinates increase as the joystick moves to the right, while
vertical coordinates increase as the joystick moves downward. If fg_initjoy
did not initialize the specified joystick, or if your program hasn't yet
called fg_initjoy, both fg_getxjoy and fg_getyjoy will return the value -1.
Joystick characteristics vary more than those of any other input device.
The values returned by fg_getxjoy and fg_getyjoy depend on the system's
processor speed and the brand of joystick used. It often suffices to know
the joystick position relative to its previous position, in which case the
actual coordinate values do not matter. However, if you must rely on
specific coordinate values, your program must perform some type of manual
joystick calibration and then scale the coordinates reported by fg_getxjoy
and fg_getyjoy as needed.
The other piece of information joysticks provide is the button status.
Most joysticks have two buttons, called the top and bottom buttons. Others
have three buttons, but one of them duplicates the functionality of another
(for example, a joystick might have one bottom button on its left side and
another on its right side). The Fastgraph routine fg_button returns the
joystick button status as its function value. Like fg_getxjoy and
fg_getyjoy, the fg_button routine requires a single argument that specifies
the joystick number. The meaning of the returned value is shown below.
value meaning
0 neither button pressed
1 top button pressed
2 bottom button pressed
3 top and bottom buttons pressed
You don't need to call fg_initjoy before using fg_button. If the
specified joystick is not present, the fg_button routine will return a value
of 0.
Example 12-11 uses fg_getxjoy, fg_getyjoy, and fg_button to poll both
joysticks at half-second intervals. It then displays the joystick number (1
or 2), horizontal position, vertical position, and button status for each
joystick. As the program runs, you can move the joysticks and watch how the
movements affect the displayed coordinate values. The program continues
doing this until you press Ctrl/C or Ctrl/Break to stop it.
Chapter 12: Input Device Support 229
Example 12-11.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
void main()
{
int b, x, y;
fg_initjoy(1);
fg_initjoy(2);
while (1) {
x = fg_getxjoy(1);
y = fg_getyjoy(1);
b = fg_button(1);
printf("1: %3d %3d %1d\n",x,y,b);
x = fg_getxjoy(2);
y = fg_getyjoy(2);
b = fg_button(2);
printf("2: %3d %3d %1d\n\n",x,y,b);
fg_waitfor(9);
}
}
There are two ways of effectively monitoring joystick button status.
One is to call fg_button at many places in your program and then take the
necessary action depending on the button status. However, the preferable
method is to extend the BIOS time-of-day interrupt to check the button status
at each clock tick (there are 18.2 clock ticks per second), set a flag if a
button is pressed, and then check the flag as needed in your program.
Information on changing the BIOS time-of-day interrupt appears in Appendix C
of this document.
Keyboard Emulation
Although we can use the fg_getxjoy and fg_getyjoy routines to monitor
relative joystick movements, it is usually easier to do this with another
Fastgraph routine, fg_intjoy. This routine is similar to the fg_intkey
routine in that it returns two values that are equivalent to the standard or
extended keyboard codes for analogous keystrokes.
The fg_intjoy routine needs three arguments. The first argument
specifies the joystick number, either 1 or 2. The second and third
arguments, both one-byte quantities passed by reference, receive the standard
and extended keyboard codes analogous to the joystick movement and button
status. The second argument receives a value of 13 (the standard keyboard
code for the Enter key) if any joystick button is pressed; it receives a
value of 0 if not. The third argument receives a value corresponding to the
extended keyboard code for one of the directional keys on the numeric keypad,
as summarized in the following table.
230 Fastgraph User's Guide
joystick position corresponding key extended key code
up and left Home 71
up up arrow 72
up and right PgUp 73
left left arrow 75
centered (no action) 0
right right arrow 77
down and left End 79
down down arrow 80
down and right PgDn 81
The fg_intjoy routine will set both key code arguments to zero if the
specified joystick has not yet been initialized.
Example 12-12 is similar to example 12-10, but it uses fg_intjoy in
place of fg_getxjoy and fg_getyjoy to report relative joystick position.
This program does not report the joystick button status as example 12-10
does, but you could readily add this feature to it.
Example 12-12.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
void main()
{
char key, aux;
fg_initjoy(1);
fg_initjoy(2);
while (1) {
fg_intjoy(1,&key,&aux);
printf("1: %2d %2d\n",key,aux);
fg_intjoy(2,&key,&aux);
printf("2: %2d %2d\n\n",key,aux);
fg_waitfor(9);
}
}
Special Joystick Considerations
If you develop a program that supports only one joystick, you should use
joystick 1. The reasons for this are twofold. First, it will make your
program consistent with most other products that support joysticks. Second,
and perhaps more importantly, many Tandy 1000 series machines cannot
determine if joystick 2 is present when neither joystick is connected. This
means if you use joystick 2 instead of joystick 1 in a single joystick
program, you won't be able to tell if a joystick is available when running on
a Tandy 1000.
Chapter 12: Input Device Support 231
Summary of Input Routines
This section summarizes the functional descriptions of the Fastgraph
routines presented in this chapter. More detailed information about these
routines, including their arguments and return values, may be found in the
Fastgraph Reference Manual.
FG_BUTTON returns information about the state of either joystick's
buttons.
FG_CAPSLOCK determines the state of the CapsLock key.
FG_GETKEY waits for a keystroke (or reads the next entry from the BIOS
keyboard buffer). It returns the keystroke's standard or extended keyboard
code.
FG_GETXJOY and FG_GETYJOY return the horizontal and vertical coordinate
position of the specified joystick. The actual coordinates depend on the
processor speed and brand of joystick used.
FG_INITJOY initializes joystick 1 or 2 and must be called before using
fg_getxjoy, fg_getyjoy, or fg_intjoy. It returns a status code indicating
whether or not the initialization was successful.
FG_INTJOY returns the standard and extended keyboard codes analogous to
the current position and button status of the specified joystick.
FG_INTKEY reads the next entry from the BIOS keyboard buffer and returns
the keystroke's standard or extended keyboard code. It is similar to
fg_getkey, but it does not wait for a keystroke if the keyboard buffer is
empty.
FG_MOUSEBUT returns information about mouse button press or release
counts, as well as the mouse cursor position at the time of the last button
press or release.
FG_MOUSECUR defines the appearance of the mouse cursor in text video
modes.
FG_MOUSEINI initializes the mouse and must be called before any of
Fastgraph's other mouse support routines. It returns an error status if the
mouse driver has not been loaded, or if the mouse is not connected.
FG_MOUSELIM defines the rectangular area in which the mouse cursor may
move.
FG_MOUSEMOV moves the mouse cursor to the specified character cell (in
text modes) or screen space position (in graphics modes).
FG_MOUSEPOS returns the current mouse position and button status.
FG_MOUSEPTR defines the shape and appearance of the mouse cursor in
graphics video modes.
FG_MOUSESPD defines the number of mickey units per eight pixels of
cursor movement. This effectively controls the speed at which the mouse
cursor moves relative to the movement of the mouse itself.
232 Fastgraph User's Guide
FG_MOUSEVIS makes the mouse cursor visible or invisible.
FG_NUMLOCK determines the state of the NumLock key.
FG_SCRLOCK determines the state of the ScrollLock key (which is not
present on some keyboards).
FG_SETCAPS controls the state of the CapsLock key.
FG_SETNUM controls the state of the NumLock key.
FG_WAITKEY flushes the BIOS keyboard buffer (that is, removes any type-
ahead characters) and then waits for another keystroke.
Chapter 13
Sound Effects
234 Fastgraph User's Guide
Overview
In the realm of the IBM PC and PS/2 family of systems, a sound is
defined by its frequency, duration, and volume. The frequency of a sound is
measured in units called Hertz. While the PC and PS/2 can produce sounds
ranging from 18 to more than one million Hertz, the average human can hear
sounds between 20 and about 20,000 Hertz. The length of a sound, called its
duration, is expressed in clock ticks; there are either 18.2 of 72.8 clock
ticks per second, depending on the method used to produce the sound.
Finally, the volume determines the loudness of the sound. As we'll see in
this chapter, we can control a sound's volume only on the PCjr and Tandy 1000
systems.
Fastgraph contains several different methods for producing sound
effects. These include single tones, a series of tones expressed
numerically, or a series of tones expressed as musical notes. The sound
effects may be discrete, continuous, or performed at the same time as other
activity. The sound-related routines are independent of the other parts of
Fastgraph and do not require any initialization routines be called.
Sound Sources
All members of the PC and PS/2 families can produce sounds using the
8253-5 programmable timer chip and the internal speaker. This method is
limited to producing single sounds of given frequencies and durations,
although we can combine these sounds to create interesting audio effects or
play music. When we use this technique, we have no control over the sound
volume. In fact, sound volumes often vary slightly on different systems
because the physical properties of the speaker and its housing are not always
the same.
The PCjr and Tandy 1000 systems have an additional, more powerful chip
for producing sounds. This is the Texas Instruments SN76496A sound chip,
called the TI sound chip for short. The TI sound chip has three independent
voice channels for producing pure tones, and a fourth channel for generating
periodic or white noise. Each voice channel has a separate volume control
that allows us to control the loudness of the sound it emits.
Synchronous Sound
A sound effect is said to be synchronous if it is produced while no
other activity is being performed. In other words, a program makes a
synchronous sound by starting the sound, waiting for a specified duration,
and then stopping the sound. The program must wait for the sound to complete
before doing anything else. As long as the duration is relatively short, the
fact that the sound is synchronous has little or no effect on the program's
execution speed. Fastgraph includes routines for producing synchronous sound
using either the 8253-5 programmable timer or the TI sound chip.
The fg_sound routine uses the programmable timer to produce a sound of a
given frequency and duration. The frequency, defined by the first argument,
is expressed in Hertz and must be an integer value between 18 and 32,767.
The second argument defines the duration and is expressed in clock ticks;
there are 18.2 clock ticks per second. If the duration is zero or negative,
the sound will continue until it is stopped with the fg_quiet routine.
Chapter 13: Sound Effects 235
Example 13-1 uses the fg_sound routine to create different sound
effects, pausing for one second between each. It first produces three
distinct sounds of 20, 100, and 1,000 Hertz. Each of these sounds lasts for
approximately 1/6 of a second (three clock ticks). The program then makes a
warbling noise by quickly alternating sounds of similar frequencies.
Finally, the program creates a sliding tone of increasing frequencies between
100 and 500 Hertz. Each tone in this sequence lasts for two clock ticks, so
it takes about 4.5 seconds to play the entire sequence. In all cases,
example 13-1 displays an identifying message just before each sound.
Example 13-1.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
void main()
{
int freq;
printf("20 Hz tone...\n");
fg_sound(20,3);
fg_waitfor(18);
printf("100 Hz tone...\n");
fg_sound(100,3);
fg_waitfor(18);
printf("1000 Hz tone...\n");
fg_sound(1000,3);
fg_waitfor(18);
printf("warble...\n");
fg_sound(400,1);
fg_sound(410,1);
fg_sound(400,1);
fg_sound(410,1);
fg_waitfor(18);
printf("sliding tone from 100 to 500 Hz...\n");
for (freq = 100; freq <= 500; freq+=10)
fg_sound(freq,2);
}
The fg_voice routine is analogous to the fg_sound routine, but it uses
the TI sound chip rather than the programmable timer to create sound. For
this reason, the fg_voice routine can only be used on the PCjr or Tandy 1000
systems. The TI sound chip allows us to control the volume of a sound, and
it also offers four distinct voice channels. Thus, fg_voice requires two
additional arguments besides frequency and duration to define the voice
channel and sound volume.
The first argument to fg_voice defines the voice channel, as shown
below.
236 Fastgraph User's Guide
value meaning
1 voice channel #1
2 voice channel #2
3 voice channel #3
4 voice channel #4, periodic noise
5 voice channel #4, white noise
If we use voice channels 1, 2, or 3, the second argument defines the sound
frequency in Hertz, between 18 and 32,767. If we use voice channel 4,
however, the second argument instead is a value that represents a specific
frequency, as shown in this table.
value frequency
0 512 Hertz
1 1024 Hertz
2 2048 Hertz
The third argument defines the sound volume. It must be between 0 and 15,
where 0 is silent and 15 is loudest. The fourth argument defines the sound
duration in clock ticks. As with the fg_sound routine, there are 18.2 clock
ticks per second, and if the duration is zero or negative, the sound will
continue until stopped with the fg_quiet routine.
Example 13-2 uses the fg_voice routine to create different sound effects
using the TI sound chip. As in example 13-1, there is a pause of one second
between each. The program first calls the fg_testmode routine to be sure it
is running on a PCjr or Tandy 1000 system (video mode 9 is only available on
these systems). If so, the program uses voice channel #4 to produce a 2,048
Hertz periodic noise, followed by white noise of the same frequency. Both
sounds are emitted at the maximum volume level (15) and last for about 1/6 of
a second each (three clock ticks). After these noises, example 13-2 produces
a 500 Hertz tone of increasing volume. In all cases, the program displays an
identifying message just before each sound.
Example 13-2.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
int volume;
if (fg_testmode(9,0) == 0) {
Chapter 13: Sound Effects 237
printf("This program requires a PCjr or ");
printf("a Tandy 1000 system.\n");
exit(1);
}
printf("2048 Hz periodic noise...\n");
fg_voice(4,2,15,3);
fg_waitfor(18);
printf("2048 Hz white noise...\n");
fg_voice(5,2,15,3);
fg_waitfor(18);
printf("500 Hz tone of increasing volume...\n");
for (volume = 1; volume <= 15; volume++) {
fg_voice(1,500,volume,0);
fg_waitfor(4);
}
fg_quiet();
}
Note how example 13-2 uses a duration of zero (continuous sound) and the
fg_waitfor routine to specify the duration for each volume level the 500
Hertz tone sequence. This causes the transition between changes in volume to
blend better with each other. The fg_quiet routine, which stops continuous
sound started with the fg_sound or fg_voice routines, ends the sound after
the final volume level.
The fg_sound and fg_voice routines each produce a single sound. We've
seen how to combine sounds to produce sound effects, but still the individual
sounds are defined numerically -- that is, by a certain frequency and
duration. It is often easier to create sounds from musical notes, and for
this reason Fastgraph includes a routine fg_music that produces such sounds.
The fg_music routine uses the programmable timer to produce synchronous
sound; it does not support the TI sound chip.
The fg_music routine has a single argument called the music string,
passed by reference as a byte array or character string. The music string is
simply a variable-length sequence of music commands, followed by a dollar-
sign ($) terminator. Music commands are summarized in the following table.
command meaning
A thru G Play the specified note in the current
octave.
# May be appended to a note character (A
through G) to make that note sharp.
. May be appended to a note character (A
through G) or a sharp (#) to extend that note
by half its normal length. Multiple dots may
be used, and each will again extend the note
by half as much as the previous extension.
238 Fastgraph User's Guide
Ln Set the length of subsequent notes and
pauses. The value of n is an integer between
1 and 64, where 1 indicates a whole note, 2 a
half note, 4 a quarter note, and so forth.
If no L command is present, L4 is assumed.
On Set the octave for subsequent notes. The
value of n may be an integer between 0 and 6
to set a specific octave. It also can be a
plus (+) or minus (-) character to increment
or decrement the current octave number.
Octave 4 contains middle C, and if no O
command is present, O4 is assumed.
P Pause (rest) for the duration specified by
the most recent L command.
Sn Set the amount of silence between notes. The
value of n is an integer between 0 and 2. If
n is 0, each note plays for the full period
set by the L command (music legato). If n is
1, each note plays for 7/8 the period set by
the L command (music normal). If n is 2,
each note plays for 3/4 the period set by the
L command (music staccato). If no S command
is present, S1 is assumed.
Tn Set the tempo of the music (the number of
quarter notes per minute). The value of n is
an integer between 32 and 255. If no T
command is present, T120 is assumed.
The fg_music routine ignores any other characters in the music string. It
also ignores command values outside the allowable range, such as T20 or O8.
Example 13-3 illustrates some uses of the fg_music routine. The program
plays the first few bars of "Mary Had a Little Lamb", followed by the musical
scale (including sharps) in two octaves, and finally the introduction to
Beethoven's Fifth Symphony. There is a pause of one second between each
piece of music, and the program displays the titles before playing the music.
Blank characters appear in the music strings to help make them more readable.
Example 13-3.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
void main()
{
printf("Mary Had a Little Lamb...\n");
fg_music("T150 L8 EDCDEEE P DDD P EGG P EDCDEEE L16 P L8 EDDEDC$");
fg_waitfor(18);
printf("up the scale in two octaves...\n");
fg_music("L16 CC#DD#EFF#GG#AA#B O+ CC#DD#EFF#GG#AA#B$");
Chapter 13: Sound Effects 239
fg_waitfor(18);
printf("Beethoven's Fifth Symphony...\n");
fg_music("T180 O2 L2 P L8 P GGG L2 D# L24 P L8 P FFF L2 D$");
}
Asynchronous Sound
Sounds made concurrently with other activity in a program are said to be
asynchronous. Fastgraph's routines that produce asynchronous sound just
start the sound and then immediately return control to the calling program.
The sounds will automatically stop when the end of the sequence is reached,
and you also can suspend or stop it on demand before that time. None of
Fastgraph's asynchronous sound routines have any effect if there is already
asynchronous sound in progress. In addition, the asynchronous sound routines
temporarily disable the synchronous sound routines (fg_sound, fg_voice, and
fg_music) while asynchronous sound is in progress.
To expand the range of sound effects and to play fast-tempo music,
Fastgraph temporarily quadruples the clock tick interrupt rate from 18.2 to
72.8 ticks per second while producing asynchronous sound. Because many disk
controllers rely on the 18.2 tick per second clock rate to synchronize disk
accesses, your programs should not perform any disk operations when
asynchronous sound is in progress.
The fg_sounds routine is the asynchronous version of the fg_sound
routine. It uses the programmable timer to play a sequence of tones
simultaneous to other operations. This routine expects as its first argument
a variable-length integer array, passed by reference, containing pairs of
frequency and duration values. As with the fg_sound routine, each frequency
is expressed in Hertz and must be between 18 and 32,767. The durations are
also measured in clock ticks, but because the interrupt rate is quadrupled,
there are 72.8 instead of 18.2 ticks per second.
The format of the frequency and duration array passed to fg_sounds is
shown below.
[0] frequency of sound 1
[1] duration of sound 1
[2] frequency of sound 2
[3] duration of sound 2
.
.
.
[2n-2] frequency of sound n
[2n-1] duration of sound n
[2n] terminator (0)
240 Fastgraph User's Guide
Note that a null character (that is, a zero byte) terminates the array. The
second argument passed to fg_sounds is an integer value indicating the number
of times to cycle through the frequency and duration array. If this value is
negative, the sounds will continue until stopped with the fg_hush or
fg_hushnext routines.
Example 13-4 uses the fg_sounds routine to play the 100 to 500 Hertz
sliding tone sequence of example 13-1. To prove the sounds are being made
concurrently with other operations, messages are displayed while the sequence
is playing. This is controlled by the Fastgraph routine fg_playing, which
returns a value of 1 if asynchronous sounds are in progress, and 0 if not.
Note how the duration must be specified as 8 clock ticks (instead of 2 as in
example 13-1) to compensate for the quadrupled clock tick interrupt rate.
Example 13-4.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
void main()
{
int i;
int freq;
int sound_array[83];
i = 0;
for (freq = 100; freq <= 500; freq+=10) {
sound_array[i++] = freq;
sound_array[i++] = 8;
}
sound_array[i] = 0;
fg_sounds(sound_array,1);
while(fg_playing())
printf("Still playing...\n");
}
Just as the fg_sounds routine is analogous to the fg_sound routine,
there is a Fastgraph routine fg_voices that is similar to the fg_voice
routine. That is, fg_voices uses the TI sound chip to play an asynchronous
sequence of tones. Its arguments are the same as those of the fg_sounds
routine, but the structure of the sound array is different. Its structure
is:
[0] channel # of sound 1
[1] frequency of sound 1
[2] volume of sound 1
[3] duration of sound 1
Chapter 13: Sound Effects 241
[4n-4] channel # of sound n
[4n-3] frequency of sound n
[4n-2] volume of sound n
[4n-1] duration of sound n
[4n] terminator (0)
The channel numbers, frequencies, volumes, and durations must be in the same
ranges as discussed in the description of the fg_voice routine, except the
durations are quadrupled because of the accelerated clock tick interrupt
rate. Again, note that a null character (that is, a zero byte) terminates
the array.
Example 13-5 uses the fg_voices routine to play the 500 Hertz tone
sequence of increasing volume introduced in example 13-2. As in example
13-4, the program displays messages while the tone sequence is playing to
demonstrate the sounds are being made concurrently with other operations.
Note how the duration is now 16 clock ticks (instead of 4 as in example 13-2)
because of the quadrupled clock tick interrupt rate.
Example 13-5.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
void main()
{
int voice_array[61];
int i;
int volume;
if (fg_testmode(9,0) == 0) {
printf("This program requires a PCjr or ");
printf("a Tandy 1000 system.\n");
exit(1);
}
i = 0;
for (volume = 1; volume <= 15; volume++) {
voice_array[i++] = 1; /* use channel 1 */
voice_array[i++] = 500; /* 500 Hz frequency */
voice_array[i++] = volume; /* variable volume */
voice_array[i++] = 16; /* duration */
}
voice_array[i] = 0;
fg_voices(voice_array,1);
242 Fastgraph User's Guide
while(fg_playing())
printf("Still playing...\n");
}
There is also an asynchronous version of the fg_music routine. It is
called fg_musicb, and it uses the same format music string as the fg_music
routine does. However, the fg_musicb routine has a second argument that
specifies the number of times to cycle through the music string. If this
value is negative, the music will play repetitively until you stop it with
the fg_hush or fg_hushnext routine.
Example 13-6 plays the same three pieces of music as example 13-3, but
it does so concurrently with other operations. As the music plays, the
program continuously displays the title of each piece. Note how we can take
advantage of the repetition in the music string for the "up the scale"
sequence by playing the sequence twice.
Example 13-6.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
void main()
{
fg_musicb("T150 L8 EDCDEEE P DDD P EGG P EDCDEEE L16 P L8 EDDEDC$",1);
while (fg_playing())
printf("Mary Had a Little Lamb...\n");
fg_waitfor(18);
fg_musicb("L16 CC#DD#EFF#GG#AA#B O+$",2);
while (fg_playing())
printf("up the scale in two octaves...\n");
fg_waitfor(18);
fg_musicb("T180 O2 L2 P L8 P GGG L2 D# L24 P L8 P FFF L2 D$",1);
while (fg_playing())
printf("Beethoven's Fifth Symphony...\n");
}
The next example demonstrates the effects of the Fastgraph routines
fg_hush and fg_hushnext, which stop sounds started with the fg_sounds,
fg_voices, or fg_musicb routines. The fg_hush routine immediately stops
asynchronous sound, whereas the fg_hushnext routine does so when the current
cycle finishes. Neither routine has any arguments, and neither routine has
any effect if no asynchronous sound is in progress. Furthermore, note that
fg_hushnext has no effect unless the asynchronous sound is continuous.
Example 13-7 runs in any text or graphics video mode. It displays
rectangles in up to 16 colors while playing continuous asynchronous music.
The program periodically checks for keystrokes with the fg_intkey routine,
and it continues to play the music while there is no keyboard activity. If
you press the Escape key, the program uses fg_hush to stop the music
immediately; this causes an exit from the while loop. If you press any other
Chapter 13: Sound Effects 243
key, the program uses fg_hushnext to stop the music as soon as the current
repetition finishes. Once it does, the program exits the while loop because
fg_playing will return a value of zero.
Example 13-7.
#include <fastgraf.h>
void main(void);
#define ESC 27
void main()
{
int color;
int old_mode;
unsigned char key, aux;
old_mode = fg_getmode();
fg_setmode(fg_automode());
color = 0;
fg_musicb("O4 L16 CC#DD#EFF#GG#AA#B O+ CC#DD#EFF#GG#AA#B$",-1);
while (fg_playing())
{
color = (color + 1) & 15;
fg_setcolor(color);
fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
fg_waitfor(4);
fg_intkey(&key,&aux);
if (key == ESC)
fg_hush();
else if (key+aux != 0)
fg_hushnext();
}
fg_setmode(old_mode);
fg_reset();
}
Example 13-7 also demonstrates an important side-effect of the fg_musicb
routine when playing continuous music. Any length, octave, silence, or tempo
values changed within the string are not reset to their original values at
the beginning of each repetition. If we did not include the O4 command at
the beginning of the string, the later O+ command would cause the music to
play in octaves 4 and 5 during the first repetition, 5 and 6 during the
second repetition, and octave 6 for all subsequent repetitions (because you
cannot increase the octave number above 6).
The final two routines relating to asynchronous sound are fg_resume and
fg_suspend. The fg_suspend routine suspends music previously started by
fg_musicb, while fg_resume restarts the music from the point where it was
suspended. Example 13-8 plays the first few bars of "Mary Had a Little
Lamb". If you press any key while the song is playing, it stops. Then,
after another keystroke, the music resumes and continues until finished.
244 Fastgraph User's Guide
Example 13-8.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
void main()
{
fg_musicb("T150 L8 EDCDEEE P DDD P EGG P EDCDEEE L16 P L8 EDDEDC$",1);
fg_waitkey();
fg_suspend();
printf("Music suspended. Press any key to resume.\n");
fg_waitkey();
fg_resume();
printf("Music resumed.\n");
while (fg_playing());
printf("Music finished.\n");
}
The fg_suspend routine has no effect if there is no asynchronous music in
progress. Similarly, fg_resume has no effect if there is no suspended music.
If you call fg_suspend and then need to cancel the music or exit to DOS
instead of restarting the music, call fg_hush instead of fg_resume.
Summary of Sound Routines
This section summarizes the functional descriptions of the Fastgraph
routines presented in this chapter. More detailed information about these
routines, including their arguments and return values, may be found in the
Fastgraph Reference Manual.
FG_HUSH immediately stops asynchronous sound started with the fg_sounds,
fg_voices, or fg_musicb routines.
FG_HUSHNEXT is similar to fg_hush, but it does not stop the asynchronous
sound until the current repetition finishes.
FG_MUSIC uses the programmable timer to play a sequence of musical
tones.
FG_MUSICB is the asynchronous version of the fg_music routine. It uses
the programmable timer to play a sequence of musical tones, concurrent with
other activity.
FG_PLAYING determines whether or not there is any asynchronous sound in
progress.
FG_QUIET stops continuous synchronous sound started with the fg_sound or
fg_voice routines.
FG_RESUME restarts asynchronous music previously suspended by
fg_suspend.
Chapter 13: Sound Effects 245
FG_SOUND produces a tone of a specified frequency and duration using the
programmable timer.
FG_SOUNDS is the asynchronous version of the fg_sound routine. It can
play a series of tones of specified frequencies and durations, concurrent
with other activity.
FG_SUSPEND suspends asynchronous music previously started by fg_musicb.
FG_VOICE produces a tone of a specified frequency, duration, and volume
using one of the TI sound chip's four voice channels.
FG_VOICES is the asynchronous version of the fg_voice routine. It can
play a series of tones of specified frequencies, durations, and volumes,
concurrent with other activity.
246 Fastgraph User's Guide
Chapter 14
Program Timing
248 Fastgraph User's Guide
Overview
It is occasionally necessary to delay a program's execution for a brief
period, or to determine how long it takes to execute specific sections of a
program. Fastgraph includes routines to accomplish these tasks. Some of
these routines are said to be real-time, which means they are independent of
a system's processor speed, while the speed of others is processor-specific.
This chapter describes both classes of timing routines, all of which are
independent of the other parts of Fastgraph.
Real-Time Routines
Real-time operations center around the BIOS time-of-day clock, which is
nothing more than a counter that the system automatically increments 18.2
times per second. This number is often called the clock tick interrupt rate
because an interrupt routine performs the incrementing. In addition, each
increment is usually called a clock tick.
The Fastgraph routine fg_waitfor delays a program's execution by the
number of clock ticks specified as its argument. Because fg_waitfor uses
clock ticks, the actual length of the delay is the same, regardless of the
system's processor speed. Even when Fastgraph's asynchronous sound routines
quadruple the clock tick interrupt rate, Fastgraph compensates for this
internally so fg_waitfor always works as though the actual rate were still
18.2 times per second.
Example 14-1 displays a message every five seconds that states how long
the program has been running. The fg_waitfor routine produces the five-
second delay by pausing 91 (18.2 times 5) clock ticks before the program
displays each message. The program returns to DOS when you press any key.
Example 14-1.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
void main()
{
unsigned int seconds;
unsigned char key, aux;
seconds = 0;
do {
fg_waitfor(91);
seconds += 5;
printf("%u seconds have elapsed.\n",seconds);
fg_intkey(&key,&aux);
}
while (key+aux == 0);
}
Another common application of the fg_waitfor routine is to slow down a
loop that uses the fg_intkey routine to check for keystrokes. In loops that
Chapter 14: Program Timing 249
do little else, we may call fg_intkey too rapidly without this delay, and it
is then possible that the BIOS may not be able to store characters in its
keyboard buffer fast enough. A small delay, even one clock tick, often helps
such "tight" loops.
The fg_getclock routine provides an efficient way to measure time,
especially differences in time. This routine has no arguments and returns a
32-bit unsigned integer (as its function value) representing the number of
clock ticks since midnight. Example 14-2 demonstrates the fg_getclock
routine. In response to any keystroke (except Escape, which returns control
to DOS), the program displays the number of clock ticks since midnight, and
the number of ticks since the program started.
Example 14-2.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
#define ESC 27
void main()
{
unsigned long start, ticks;
unsigned char key, aux;
start = fg_getclock();
do {
ticks = fg_getclock();
printf("%lu ticks since midnight.\n",ticks);
printf("%lu ticks since start of program.\n\n",ticks-start);
fg_getkey(&key,&aux);
}
while (key != ESC);
}
Routines Dependent on the System Speed
The fg_waitfor routine described in the previous section is independent
of the system's processor speed. This means the actual length of its delay
is the same on any system. Another routine, fg_stall, is similar to
fg_waitfor, but its delay is proportional to the processor speed. Like
fg_waitfor, fg_stall has a single integer argument that specifies the length
of the delay. However, instead of being expressed in clock ticks, fg_stall
measures the delay in delay units. The fg_stall routine treats the length as
an unsigned quantity, so the maximum number of delay units we can specify is
65,535. The following table lists the approximate number of delay units per
clock tick on three typical systems.
250 Fastgraph User's Guide
system delay units
type per clock tick
Tandy 1000 HX 675
10 MHz 80286 3,000
25 MHz 80386 11,000
Fastgraph includes a routine that determines the number of delay units
per clock tick for the processor being used. This is the fg_measure routine,
which has no arguments and returns the number of delay units per clock tick
as its function value. Once we determine this value, we can use fg_stall to
delay a program's execution in real time. This provides a much more refined
delay than the clock tick unit used by fg_waitfor.
Example 14-3 is functionally identical to example 14-1, but it uses the
fg_stall routine instead of fg_waitfor to delay the program execution. The
program first calls the fg_measure routine to determine number of delay units
equivalent to one clock tick. It then passes this value to fg_stall, called
91 times inside the for loop to create the five-second delay (because 91
clock ticks equals five seconds). The program returns to DOS when you press
any key.
Example 14-3.
#include <fastgraf.h>
#include <stdio.h>
void main(void);
void main()
{
int i;
int units_per_tick;
unsigned int seconds;
unsigned char key, aux;
seconds = 0;
printf("Benchmarking system speed...\n");
units_per_tick = fg_measure();
printf("Benchmark completed.\n\n");
do {
for (i = 0; i < 91; i++)
fg_stall(units_per_tick);
seconds += 5;
printf("%u seconds have elapsed.\n",seconds);
fg_intkey(&key,&aux);
}
while (key+aux == 0);
}
One final point: the fg_measure routine takes a few seconds to
benchmark the system speed accurately. For this reason, you should only call
fg_measure once (typically at the beginning of the program) and use its
return value instead of calling fg_measure throughout the program.
Chapter 14: Program Timing 251
Summary of Timing Routines
This section summarizes the functional descriptions of the Fastgraph
routines presented in this chapter. More detailed information about these
routines, including their arguments and return values, may be found in the
Fastgraph Reference Manual.
FG_GETCLOCK returns the number of clock ticks since midnight as its
function value. This quantity is a 32-bit unsigned integer.
FG_MEASURE returns the approximate number of delay units per clock tick
as its function value. This quantity is proportional to the system's
processor speed.
FG_STALL delays a program's execution for a given number of processor-
specific delay units.
FG_WAITFOR delays a program's execution for a given number of clock
ticks. There are 18.2 clock ticks per second, regardless of the system's
processor speed.
252 Fastgraph User's Guide
Chapter 15
Miscellaneous Routines
254 Fastgraph User's Guide
Overview
There are a few remaining Fastgraph routines that really don't fit into
any of the categories discussed so far. For this reason, they are described
separately in this chapter.
Determining Available Memory
The fg_memavail routine returns the amount of free conventional memory
(in bytes) available to DOS. It returns the amount of memory as its function
value, which is a 32-bit unsigned integer. Fg_memavail has no arguments.
Example 15-1 uses fg_memavail to show the effects of creating and
releasing virtual pages. When run in a video mode in which video pages 1 and
2 are physical pages, the amount of free memory remains the same because
these pages use memory that is resident on the video adapter. However, in
modes where pages 1 and 2 are virtual pages, the amount of free memory
decreases after each call to fg_allocate and returns to its original value
after the calls to fg_freepage. Note how the program requests and validates
the video mode.
Example 15-1.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main()
{
long original, mem0, mem1, mem2;
int mode, old_mode;
printf("Which video mode? ");
scanf("%d",&mode);
if (fg_testmode(mode,0) == 0) {
printf("Your system does not support that video mode.\n");
exit(1);
}
if (fg_testmode(mode,3) == 0) {
printf("Your system does not have enough memory.\n");
exit(1);
}
original = fg_memavail();
old_mode = fg_getmode();
fg_setmode(mode);
mem0 = fg_memavail();
fg_allocate(1);
mem1 = fg_memavail();
fg_allocate(2);
mem2 = fg_memavail();
fg_freepage(1);
fg_freepage(2);
Chapter 15: Miscellaneous Routines 255
fg_setmode(old_mode);
fg_reset();
printf("originally = %ld\n",original);
printf("after setmode = %ld\n",mem0);
printf("after 1st page = %ld\n",mem1);
printf("after 2nd page = %ld\n",mem2);
printf("at end = %ld\n",memavail());
}
Choosing the Video Memory Update Function
In Chapter 10, we saw how to use the fg_setfunc routine to perform XOR
animation in native EGA and VGA graphics modes (modes 13 to 18). In these
video modes, fg_setfunc controls the logical operation applied when the
contents of video memory change. The specific operation is defined by its
argument, as shown below.
value of logical
argument operation
0 replacement
1 and
2 or
3 exclusive or
If your program does not use the fg_setfunc routine, replacement mode is
always used. That is, information written to video memory replaces whatever
was there before. The fg_setfunc routine does nothing in CGA, Tandy/PCjr,
Hercules, or MCGA graphics modes, or in any text modes.
Example 15-2 demonstrates the fg_setfunc routine. The program is
similar to example 6-10 which displays 200 random rectangles on the screen.
However, example 15-2 displays the rectangles in XOR mode, which means the
rectangle intersections will appear in different colors.
Example 15-2.
#include <fastgraf.h>
#include <stdio.h>
#include <stdlib.h>
void main(void);
#define RECTANGLES 200
#define SWAP(a,b,temp) { temp = a; a = b; b = temp; }
void main()
{
int i;
int minx, maxx, miny, maxy;
int old_mode;
int temp;
256 Fastgraph User's Guide
int xres, yres;
if (fg_egacheck() == 0) {
printf("This program requires EGA or VGA.\n");
exit(1);
}
old_mode = fg_getmode();
fg_setmode(fg_automode());
fg_setfunc(3);
xres = fg_getmaxx() + 1;
yres = fg_getmaxy() + 1;
for (i = 0; i < RECTANGLES; i++) {
minx = rand() % xres;
maxx = rand() % xres;
miny = rand() % yres;
maxy = rand() % yres;
if (minx > maxx)
SWAP(minx,maxx,temp);
if (miny > maxy)
SWAP(miny,maxy,temp);
fg_setcolor(rand()%16);
fg_rect(minx,maxx,miny,maxy);
}
fg_setmode(old_mode);
fg_reset();
}
Summary of Miscellaneous Routines
This section summarizes the functional descriptions of the Fastgraph
routines presented in this chapter. More detailed information about these
routines, including their arguments and return values, may be found in the
Fastgraph Reference Manual.
FG_MEMAVAIL returns the amount of memory available to DOS.
FG_SETFUNC specifies the logical operation (replacement, or, and,
exclusive or) applied when video memory changes in the native EGA and VGA
graphics modes. This routine has no effect in other video modes.
Appendix A
Fastgraph Utilities
258 Fastgraph User's Guide
Overview
This appendix describes utilities for creating and managing pixel run
files used with the fg_dispfile routine. By default, the Fastgraph
installation procedure places these utilities in the \FG directory. To use
these utilities, you must either (1) copy the .EXE file from \FG to your
current directory, (2) make \FG your current directory, or (3) include the
\FG directory in your DOS path specification.
SNAPSHOT Utility
The SNAPSHOT utility is a terminate and stay resident program (TSR) to
capture graphic images. It stores the image in Fastgraph's standard pixel
run format. To load SNAPSHOT, just enter the command SNAPSHOT at the DOS
prompt, and you'll see messages similar to the following if SNAPSHOT loads
successfully.
C> SNAPSHOT
SNAPSHOT Version 1.01
Copyright (c) 1991 Ted Gruber Software. All Rights Reserved.
Press <alt>-<left shift> to activate.
After SNAPSHOT loads, control returns to the DOS prompt. At this point,
you can use any method whatsoever to display a graphic image and then press
the Alt and left shift keys at the same time to capture the image. You don't
need to load SNAPSHOT for each image capture, just once per system boot.
SNAPSHOT uses about 14,000 bytes of conventional memory once loaded.
To illustrate the use of SNAPSHOT, suppose you have drawn and saved an
image with a commercial paint program, and you want to incorporate this image
into a Fastgraph application. Once you load SNAPSHOT, start the paint
program and retrieve your image. Then press the Alt and left shift keys
simultaneously and wait for the success tone (three quick medium-pitched
sounds). Finally, exit the paint program to return to the DOS prompt.
The sequence described in the preceding paragraph will store the
captured image in Fastgraph's standard pixel run format, in a file named
SNAPSHOT.nnn in the current directory. The file type nnn will be the first
sequence of digits that does not result in a duplicate file name. That is,
if there are no captured image files in the current directory, SNAPSHOT will
use the file name SNAPSHOT.000. The next time you capture an image, SNAPSHOT
will store it in SNAPSHOT.001, then SNAPSHOT.002, and so forth. If you
rename or delete one of these files, SNAPSHOT will again use that file name.
For example, if you delete SNAPSHOT.000 but keep SNAPSHOT.001, SNAPSHOT will
store the next image it captures in SNAPSHOT.000.
If SNAPSHOT is unable to capture the image, it will produce its error
tone (a single low-pitched sound). The most common cause of this is trying
to capture an image from a text video mode, but it also will occur if there
is not enough disk space or if all 1,000 image file names are already being
used.
Appendix A: Fastgraph Utilities 259
CLIP Utility
The SNAPSHOT utility described in the previous section captures the
entire screen. While this might be desirable in some cases, other times
you'll just need a portion of the screen. CLIP is an interactive utility
that you can use to reduce the size of any image stored in Fastgraph's
standard or packed pixel run format. The syntax of the command for invoking
the CLIP utility from the DOS prompt is
CLIP input_file output_file options
where input_file is the name of the original image file, and output_file is
the name of the new image file. CLIP does not modify the input_file in any
way, but it will overwrite the output_file if an identically named file
exists in the current directory. The options list specifies one or more
optional switches as shown below.
option meaning
/M:mode Specifies the video mode number in which to
display the image. The mode value must be an
integer between 0 and 23. If that video mode
is a text mode, an unsupported graphics mode,
or an unavailable graphics mode, CLIP
displays an error message stating this. If
the /M switch is not present, CLIP uses the
first available video mode from the list 16,
15, 19, 13, 9, 4, 12.
/P Indicates the input_file is in Fastgraph's
packed pixel run format. If the /P switch is
not present, CLIP assumes it is in standard
pixel run format. The output_file will be in
the same format as the input_file.
/W:width Specifies the image width in pixels. The
width value must be an integer between 1 and
the horizontal resolution of the selected
video mode. If the /W switch is not present,
CLIP uses the horizontal resolution of the
selected video mode.
For example, if you wanted to create the image file PARTIAL.PPR from the
packed pixel run file SCREEN.PPR, and use the native 320 by 200 EGA graphics
video mode (mode 13), you would start CLIP with the following command.
CLIP PARTIAL.PPR SCREEN.PPR /P /M:13
Because no /W switch appears in the above command and the horizontal
resolution of mode 13 is 320 pixels, CLIP assumes the image width is 320
pixels.
260 Fastgraph User's Guide
When CLIP displays the image and the plus-shaped cursor, you are ready
to define one corner of the clipping region (that part of the image used to
create the output_file). To do this, use the directional keys on the numeric
keypad to move the cursor to the desired position, then press the Enter key.
You are then ready to define the clipping region's opposite corner. Again,
use the directional keys to move the cursor to the desired position. When
defining the second corner, however, CLIP uses a rectangular box instead of
the plus-shaped cursor to simplify marking the clipping region's boundaries.
After you press Enter to define the second corner, CLIP creates the
output_file and displays the resulting image width and the number of pixel
runs the image contains.
CLIP includes some features that may help you define the clipping
region. You can change the distance the cursor moves in response to the
directional keys, display the current (x,y) pixel coordinates of the cursor,
and change the cursor color. The following table explains the keystrokes
that CLIP recognizes when you are defining the clipping region.
key meaning
F1 Displays the (x,y) coordinate bar at the top
of the screen. If the coordinate bar is
already on, F1 removes it.
F2 Displays the (x,y) coordinate bar at the
bottom of the screen. If the coordinate bar
is already on, F2 removes it.
F3 Changes the cursor or box color from white to
black, or from black to white.
F4 Displays a summary of the keys CLIP
recognizes when defining the clipping region.
KP1 Moves the cursor one unit down and to the
left.
KP2 Moves the cursor one unit down.
KP3 Moves the cursor one unit down and to the
right.
KP4 Moves the cursor one unit to the left.
KP6 Moves the cursor one unit to the right.
KP7 Moves the cursor one unit up and to the left.
KP8 Moves the cursor one unit up.
KP9 Moves the cursor one unit up and to the
right.
+ Increases the unit of cursor movement by one
pixel. The default cursor movement is one
pixel.
- Decreases the unit of cursor movement by one
pixel.
Enter Defines a corner of the clipping region
at the cursor position.
Esc Exits to DOS without creating the
output_file. CLIP will first issue an "Exit
to DOS?" prompt in case you pressed the Esc
key accidentally.
Appendix A: Fastgraph Utilities 261
The CLIP utility requires two video pages to run. Thus, you cannot use
it in video modes 17, 18, and 23 because they have only one physical video
page and no virtual video pages.
CONVERT Utility
The CONVERT utility lets you translate files between Fastgraph's
supported image file formats. The syntax of the command for invoking CONVERT
from the DOS prompt is
CONVERT input_file output_file
where input_file is the name of the original image file, and output_file is
the name of the new translated image file. CONVERT does not modify the
input_file in any way, but it will overwrite the output_file if an
identically named file exists in the current directory.
By default, the file type of the input_file and output_file determine
the image format of that file. If the file type is .PPR, CONVERT assumes the
image is in Fastgraph's packed pixel run format. If the file type is .SPR,
CONVERT assumes it is in the Fastgraph's standard pixel run format. If your
image files use other file types, you can explicitly specify the file's image
format by appending one of the switches /PPR or /SPR to the file name. The
input_file and output_file must not both specify the same image format
(CONVERT will display an error message if this is so).
The following command will translate the standard pixel run file
PICTURE.SPR to packed format. The packed image will be stored in the file
PICTURE.IMG, so we must append the switch /PPR to tell CONVERT that it will
be a packed file.
CONVERT PICTURE.SPR PICTURE.IMG/PPR
EDITSPR Utility
The EDITSPR utility changes all pixel runs of one color to another color
in an image file stored in Fastgraph's standard pixel run (.SPR) format. The
syntax of the command for invoking the EDITSPR utility from the DOS command
prompt is
EDITSPR input_file output_file
where input_file is the name of the original image file, and output_file is
the name of the new image file. EDITSPR does not modify the input_file in
any way, but it will overwrite the output_file if an identically named file
exists in the current directory.
After it reads the pixel runs from the input_file, EDITSPR will perform
the requested color changes. It does this iteratively by asking for an old
color value followed by a new color value (each value must be between 0 and
262 Fastgraph User's Guide
255). EDITSPR then finds the pixel runs of the old color value and changes
them to the new color value. Following this, EDITSPR displays a message
stating how many pixel runs it changed. This process repeats until you enter
a negative number for either color value.
EDITSPR will next combine adjacent pixel runs of like colors. For
example, suppose the original image file contained a color 1 pixel run of
length 50, followed by a color 2 pixel run of length 20, followed by another
color 1 pixel run of length 10. If you changed all color 2 pixel runs to
color 1, EDITSPR will combine these three pixel runs into a single run of
length 80.
Finally, EDITSPR will close the output_file.
GrabRGB Utility
The GrabRGB utility is a terminate and stay resident program (TSR) to
capture the current red, green, and blue color components of video DAC
registers in the 256-color MCGA and VGA graphics modes. You can use GrabRGB
together with Fastgraph's SNAPSHOT utility to preserve the original colors of
a captured image.
To load GrabRGB, just enter the command GRABRGB at the DOS prompt.
After GrabRGB loads, control returns to the DOS prompt. At this point, you
can use any method whatsoever to display a 256-color graphic image and then
press the Alt and right shift keys at the same time to capture the current
DAC values. You don't need to load GrabRGB for each image, just once per
system boot. GrabRGB uses about 15,000 bytes of conventional memory once
loaded.
To illustrate the use of GrabRGB, suppose you have drawn and saved a
256-color image with a commercial paint program, and you want to incorporate
this image into a Fastgraph application. Once you load SNAPSHOT and GrabRGB,
start the paint program and retrieve your image. Then press the Alt and left
shift keys to capture the image with SNAPSHOT. After SNAPSHOT's success tone
(three quick medium-pitched sounds), press Alt and right shift to capture the
RGB components of each DAC register with GrabRGB, and wait for GrabRGB's
success tone. Finally, exit the paint program and return to the DOS prompt.
The sequence described in the preceding paragraph will write the RGB
color components for each DAC register to a file named GRABRGB.nnn in the
current directory. The file type nnn will be the first sequence of digits
that does not result in a duplicate file name. That is, if there are no
GrabRGB output files in the current directory, GrabRGB will use the file name
GRABRGB.000. The next time you use GrabRGB, it will store the RGB
information in GRABRGB.001, then GRABRGB.002, and so forth. If you rename or
delete one of these files, GrabRGB will again use that file name. For
example, if you delete GRABRGB.000 but keep GRABRGB.001, GrabRGB will next
use the file name GRABRGB.000.
If GrabRGB is unable to obtain the RGB components of each DAC register,
it will produce its error tone (a single low-pitched sound). The most common
cause of this is trying to capture an image from a text video mode, or from a
graphics video mode with fewer than 256 colors. It also will occur if there
is not enough disk space or if all 1,000 output file names are already being
used.
Appendix A: Fastgraph Utilities 263
Each line in the output file created by GrabRGB is of the form
nnn,rr,gg,bb,
where nnn is a DAC register index (between 0 and 255), rr is the red
component of that DAC register, gg is the green component, and bb is the blue
component. Each color component is between 0 and 63. You can edit and
reformat these lines as necessary for inclusion in a C initializer list, a
QuickBASIC or FORTRAN data statement, or a Turbo Pascal array-type constant
list. Such an array of RGB components, but without the nnn indices, is in
the format expected by fg_setdacs.
By default, GrabRGB captures information for all 256 DAC registers. If
you want to consider only the DAC registers with color components different
from their initial values, just include the /D option when you load GrabRGB
(that is, use the command GRABRGB /D). If you specify the /D option and all
256 DACs use their default values, the output file will contain a message
stating this.
HERCFIX Utility
The HERCFIX utility allows you to use SNAPSHOT (and possibly other TSRs)
with programs that do not update the BIOS data area when establishing the
720x348 Hercules graphics mode. If you use SNAPSHOT with such a program, it
will think the monochrome text mode (video mode 7) is active and will produce
its low-pitched error tone when activated.
If this occurs, use HERCFIX to load the application from which you are
trying to capture the image. To do this, enter
HERCFIX command
at the DOS prompt, where command is the command that starts the application.
For example, suppose you use the command PAINTER /H to run a commercial paint
program in Hercules graphics mode. To load the paint program with HERCFIX,
you would enter the command HERCFIX PAINTER /H.
264 Fastgraph User's Guide
Appendix B
Using Fastgraph
from Assembly Language
266 Fastgraph User's Guide
Fastgraph uses the same naming and calling conventions as Microsoft C
and Turbo C. The details of these conventions that are important to assembly
language programming are summarized below. If you are calling Fastgraph
routines from an assembly language program, the program must follow these
conventions.
All arrays and pointers are passed by reference
All other items are passed by value
Arguments are pushed onto the stack in reverse order
16-bit function values are returned in the AX register
32-bit function values are returned in the DX:AX register pair
Fastgraph routine names are prefixed with an underscore character
The small and medium model Fastgraph libraries pass arrays and pointers by
near reference, while the large model library does so by far reference. This
is consistent with the run-time libraries for the supported compilers.
All Fastgraph routines preserve the BP, DS, DI, and SI registers. The
contents of any other registers are unknown upon return from a Fastgraph
routine (except for the AX register, which will either contain zero or the
routine's return value).
The following DOS commands show how to assemble a program (using the
Microsoft Macro Assembler) and then link it with Fastgraph. In all cases,
we'll assume the file EXAMPLE.ASM contains the source code for the program.
The resulting executable file will be called EXAMPLE.EXE.
small memory model
MASM EXAMPLE.ASM;
LINK /CP:4096 /E EXAMPLE,,NUL.MAP,FGS
medium memory model
MASM EXAMPLE.ASM;
LINK /CP:4096 /E EXAMPLE,,NUL.MAP,FGM
large memory model
MASM EXAMPLE.ASM;
LINK /CP:4096 /E EXAMPLE,,NUL.MAP,FGL
Example B-1 calls the fg_getmode, fg_setmode, fg_reset, and fg_version
routines from an assembly language program. The fg_getmode routine returns
its function value in the AX register. The fg_setmode routine has a single
argument, while fg_reset has no arguments. The fg_version routine has two
arguments, both passed by reference. Notice how they are pushed on the stack
in reverse order. This example would work with either the medium or large
memory model Fastgraph libraries. To make it work with the small model
library, all you would need to do is change the word "far" to "near" in the
EXTRN declarations, and change the name of the code segment from "main_TEXT"
to "_TEXT".
Appendix B: Using Fastgraph from Assembly Language 267
Example B-1.
EXTRN _fg_getmode:far ; Fastgraph's GETMODE routine
EXTRN _fg_reset:far ; Fastgraph's RESET routine
EXTRN _fg_setmode:far ; Fastgraph's SETMODE routine
EXTRN _fg_version:far ; Fastgraph's VERSION routine
stackseg SEGMENT stack ; suppress the linker's
stackseg ENDS ; "no stack segment" error message
_DATA SEGMENT word public 'DATA'
major dw ? ; major version number
minor dw ? ; minor version number
old_mode dw ? ; original video mode
_DATA ENDS
dgroup GROUP _DATA
ASSUME cs:main_TEXT,ds:dgroup
main_TEXT SEGMENT byte public 'CODE'
start: mov ax,_DATA ; load segment location
mov ds,ax ; into DS register
call _fg_getmode ; AX = current video mode
mov old_mode,ax ; save it
mov ax,4 ; use video mode 4
push ax ; pass argument to SETMODE
call _fg_setmode ; establish CGA four-color mode
add sp,2 ; remove SETMODE argument
push old_mode ; pass argument to SETMODE
call _fg_setmode ; restore original video mode
add sp,2 ; remove SETMODE argument
call _fg_reset ; restore screen attributes
lea ax,minor ; get address of minor variable
push ax ; pass argument #2 to VERSION
lea ax,major ; get address of major variable
push ax ; pass argument #1 to VERSION
call _fg_version ; get the Fastgraph version number
add sp,4 ; remove VERSION arguments
mov ah,76 ; function 76: terminate process
xor al,al ; errorlevel 0
int 21h ; exit to DOS
main_TEXT ENDS
END start
268 Fastgraph User's Guide
Appendix C
Interrupts and Fastgraph
270 Fastgraph User's Guide
Interrupts Used by Fastgraph
DOS maintains an interrupt vector table that contains the addresses of
256 interrupt handlers, or routines, that perform various functions. The
handlers are usually referenced by their hexadecimal interrupt number,
between 00 and FF. Of these, only interrupts 60 through 66 and F1 through FF
are not used by DOS, the ROM BIOS, or other software and are thus available
for user applications.
Certain Fastgraph routines use some of the available interrupts.
Namely, the fg_music routine uses interrupt 60, the asynchronous sound
routines (fg_musicb, fg_sounds, and fg_voices) use interrupts 60 and 61, and
all Fastgraph/Light routines use interrupt 62. If your program defines its
own interrupt handlers, it must not use any of the interrupts reserved for
Fastgraph (unless, of course, it doesn't use any of the Fastgraph routines
that would create a conflict).
Extending the Time-of-Day Interrupt
As mentioned in Chapter 14, the BIOS time-of-day clock is incremented by
an interrupt handler. The routine that does this is interrupt 08, a hardware
interrupt automatically activated 18.2 times per second. After incrementing
the clock, interrupt 08 invokes interrupt 1C, which by default references a
"do-nothing" interrupt handler. While changing interrupt 08 can be tricky,
it is fairly straightforward to define our own handler for interrupt 1C.
This handler also will be executed automatically 18.2 times per second.
Example C-1 illustrates how to do this.
When we discussed joysticks in Chapter 12, we said there were two ways
to monitor joystick button status. One is to intersperse calls to the
fg_button routine at strategic places in your program and then take necessary
action depending on the button status. However, the problem with this scheme
is the chance of missing a button press -- if you press the joystick button
and then release it between calls to fg_button, the program will not detect
the joystick activity. A preferable method is to call fg_button from a
handler for interrupt 1C, which essentially provides continuous monitoring of
the joystick buttons. When we need the button status within our program, all
we need to do is examine a global variable.
Example C-1 consists of a main program (written in C) and an assembly
language subroutine named int1C (suitable for the medium memory model). The
main program calls int1C to define a handler for interrupt 1C. In response
to any keystroke (except Escape), the program displays the button press
information for each joystick since the previous keystroke (refer to the
discussion of the fg_button routine for the meanings of the status values).
When you press the Escape key, the program exits to DOS, but not before
calling int1C to restore the original interrupt 1C handler.
Example C-1 (main program).
#include <fastgraf.h>
#include <stdio.h>
void main(void);
#define ESC 27
Appendix C: Interrupts and Fastgraph 271
int status1, status2;
void main()
{
unsigned char key, aux;
int1C(1);
status1 = 0;
status2 = 0;
do {
printf("\n");
printf("Joystick 1 status: %d\n",status1);
printf("Joystick 2 status: %d\n",status2);
status1 = 0;
status2 = 0;
fg_getkey(&key,&aux);
}
while (key != ESC);
int1C(0);
}
We'll now examine the int1C assembly language subroutine. It actually
consists of three parts: a portion to enable our interrupt handler, our
handler itself, and a portion to disable the handler. When we call int1C
with a nonzero argument, it saves the original data segment (so we can access
the global variables within the handler), saves the original handler's
address (called the vector) for interrupt 1C, and then enables our handler,
which takes the form of a far procedure.
The handler routine then begins to be activated at 18.2 times per
second. After saving all the important registers, the handler calls the
Fastgraph routine fg_button twice, once for each joystick. The return values
are logically ORed with the status1 and status2 C global variables to update
the button status information. Finally, the handler restores the original
registers and returns control to the point of the interrupt.
Before the main program exits, it calls int1C with a zero argument to
restore the original handler for interrupt 1C. No provision is made in the
program to check if we had previously defined our own handler (and hence
saved the original interrupt 1C vector), but this could be added with little
difficulty.
Example C-1 (assembly language subroutine).
EXTRN _status1:word ; C global variable for button 1 status
EXTRN _status2:word ; C global variable for button 2 status
EXTRN _fg_button:far ; Fastgraph routine
272 Fastgraph User's Guide
int1C_TEXT SEGMENT byte public 'CODE'
ASSUME cs:int1C_TEXT
int1C_CS dw ? ; holds original INT 1C segment address
int1C_IP dw ? ; holds original INT 1C offset
orig_DS dw ? ; holds original data segment
_int1C PROC far
PUBLIC _int1C
push bp ; save caller's BP register
mov bp,sp ; make BP point to argument list
push si ; save caller's SI register
push di ; save caller's DI register
mov dx,[bp+6] ; get the flag parameter
or dx,dx ; replace the old interrupt handler?
jz replace ; yes, branch to that processing
; define a new handler for INT 1C
define: mov ax,ds ; put current data segment in AX
mov cs:orig_DS,ax ; save it in the control information area
mov al,1Ch ; interrupt vector to save
mov ah,53 ; function 53: get interrupt vector
int 21h ; get the interrupt vector
mov cs:int1C_CS,es; save the segment
mov cs:int1C_IP,bx; save the offset
Appendix C: Interrupts and Fastgraph 273
push ds ; save our DS register
mov dx,offset handler ; get offset of interrupt handler
mov ax,seg handler; get segment of interrupt handler
mov ds,ax ; put it in DS
mov al,1Ch ; interrupt vector to change
mov ah,37 ; function 37: set interrupt vector
int 21h ; change the INT 1C vector to our handler
pop ds ; restore our DS register
jmp short return ; return to the caller
; replace the original handler for INT 1C
replace: push ds ; save our DS register
mov dx,cs:int1C_IP; put original INT 1C offset in DX
mov ds,cs:int1C_CS; put original INT 1C segment in DS
mov ah,37 ; function 37: set interrupt vector
mov al,1Ch ; interrupt vector 1C
int 21h ; restore original INT 1C vector
pop ds ; restore our DS register
return: xor ax,ax ; in case int1C was called as a function
pop di ; restore our DI register
pop si ; restore our SI register
pop bp ; restore our BP register
ret
_int1C ENDP
274 Fastgraph User's Guide
handler PROC far ; interrupt handler that replaces INT 1C
cli ; disable interrupts while handler active
push ax ; save registers that may be altered
push bx
push cx
push dx
push di
push si
push ds
push es
mov ds,cs:orig_DS ; retrieve the original data segment
mov ax,1 ; use joystick 1
push ax ; pass joystick number to button routine
call _fg_button ; AX = button status for joystick 1
add sp,2 ; remove the argument
or _status1,ax ; update status variable for joystick 1
mov ax,2 ; use joystick 2
push ax ; pass joystick number to button routine
call _fg_button ; AX = button status for joystick 2
add sp,2 ; remove the argument
or _status2,ax ; update status variable for joystick 2
pop es ; restore altered registers
Appendix C: Interrupts and Fastgraph 275
pop ds
pop si
pop di
pop dx
pop cx
pop bx
pop ax
iret ; return from the interrupt routine
handler ENDP
int1C_TEXT ENDS
END
The example just presented is not meant to be a tutorial on interrupts;
there are many good references on DOS that explain them in detail. However,
an example specific to Fastgraph should be helpful.
276 Fastgraph User's Guide
Appendix D
Contents of the
Compiler-Specific Libraries
278 Fastgraph User's Guide
For each of the supported Fastgraph compilers except QuickBASIC, there
is a compiler-specific Fastgraph library (also called the extended Fastgraph
library) that contains the following routines:
fg_circlew fg_getworld fg_rectw fg_setworld
fg_clprect fg_initw fg_restorew fg_swchar
fg_dashrw fg_moverw fg_savew fg_swlength
fg_dashw fg_movew fg_setangle fg_swtext
fg_drawrw fg_paintw fg_setclipw fg_xscreen
fg_draww fg_panw fg_setratio fg_xworld
fg_drectw fg_pointw fg_setsize fg_yscreen
fg_ellipsew fg_polygonw fg_setsizew fg_yworld
These routines use the world space coordinate system, either directly or
internally. Note that none of them are included in Fastgraph/Light.
As mentioned in Chapter 1, if your program uses any of these routines,
you must link it with the standard Fastgraph library (FGx.LIB) and the
compiler-specific Fastgraph library (FGcccx.LIB).
Index 279
I n d e x
8253-5 programmable timer chip 234
Active page 119
Animation 188
dynamic frame 194
dynamic page flipping 196
page flipping 196
simple 188
static frame 192
static page flipping 196
summary 198
XOR 190
ANSI.SYS 28, 29
Assembly language 266
Attribute 48, 104, 105
Available memory 254
Background color 49, 50
Bit maps 136
CGA 141, 142, 149
EGA 144, 151
filler bits 137
Hercules 144
MCGA 145
memory requirements 172
mode-independent 136
mode-specific 140
PCjr 143, 151
retrieving 169
subscript order 137, 138, 147
Tandy 143, 151
text modes 146
VGA 144, 145, 151
Bit-mapped characters 114
Borland C++ 3, 7
Byte boundary 174, 175
CapsLock 212-214
CGA palettes 51
Character cells 18
Character space 40, 100
Characters
bit-mapped 114
hardware 101
software 108
Circles 83, 84
Clearing the screen 76
CLIP 259, 260
/M option 259
/P option 259
/W option 259
Clipping 76
Clock tick 248-250
Clock tick interrupt 239, 241, 248
Closed shapes 83
Color 48
Color indices 69
280 Fastgraph User's Guide
Color number 50
Color value 50
Compilation 5
Compiler-specific Fastgraph library 278
CONVERT 261
/PPR switch 261
/SPR switch 261
Coordinate conversion 44, 107
Current color 50
Cursor mask 220, 222
Dash pattern 82
Delay units 249, 250
Display patterns 158
CGA 159, 160
EGA 161
Hercules 161
MCGA 162, 163
PCjr 160
Tandy 160
VGA 162, 163
Dithering 88
Dithering matrix 89
alignment 94
CGA 89, 90
EGA 92
Hercules 91
MCGA 93
PCjr 91
Tandy 91
VGA 92, 93
EDITSPR 261, 262
Ellipses 83, 84
EMM386.EXE 129
EMS 129
Expanded memory 129
Expanded Memory Manager 129
Extended Fastgraph library 278
Extended memory 129
Fade 66, 67
FASTGRAF.BI 6
FASTGRAF.H 5
Fastgraph 2
Fastgraph routines
fg_allocate 122, 124, 125, 127, 129, 132, 175, 178, 254
fg_alloccms 129
fg_allocems 129, 132
fg_allocxms 129, 132
fg_automode 33, 34, 36, 77, 84, 86, 206
fg_bestmode 30, 34, 35, 36, 95, 122, 124, 125
fg_box 87, 96
fg_boxdepth 87, 88, 96
fg_button 228, 229, 231, 270, 271
fg_capslock 213, 231
fg_chgattr 104, 114
fg_chgtext 104, 114
fg_circle 84, 85, 96
Fastgraph routines (continued)
fg_circlew 84, 96
fg_clipmask 166, 167, 184
fg_clpimage 148, 149, 150, 166, 167, 169, 184
fg_clprect 86, 96, 188, 195, 196
fg_clprectw 86, 96
fg_copypage 129, 131, 133, 175, 176, 184
fg_cursor 28, 36, 73, 105, 178
fg_dash 82, 96
fg_dashrel 82, 97
fg_dashrw 82, 97
fg_dashw 82, 97
fg_defcolor 70, 73, 94
fg_dispfile 157, 158, 184, 198, 258
fg_display 153-158, 184, 198
fg_displayp 154, 155, 156-158, 184, 198
fg_disppcx 164, 166, 184
fg_draw 79, 80, 97, 204
fg_drawmap 114, 136, 138-141, 152, 166, 168-172, 181, 185, 198,
222, 226
fg_drawmask 166, 167, 168, 185
fg_drawrel 79, 80, 95, 97
fg_drawrw 79, 97
fg_draww 79, 97
fg_drect 88, 89, 92, 93, 94, 97
fg_drectw 89, 94, 97
fg_drwimage 114, 141, 142-144, 146, 147, 148-150, 152, 166,
167-169, 172, 185, 198, 226
fg_egacheck 32, 36
fg_ellipse 84, 85, 97, 192
fg_ellipsew 84, 97
fg_erase 76, 97
fg_fadein 200, 201, 208
fg_fadeout 200, 208
fg_flipmask 166, 167, 185
fg_flpimage 149, 150, 166, 167, 169, 185
fg_freepage 122, 124, 125, 129, 132, 133, 254
fg_getaddr 127, 128, 133
fg_getattr 105, 114
fg_getchar 105, 114
fg_getclock 249, 251
fg_getcolor 49-51, 57, 73
fg_getdacs 66, 67, 73
fg_gethpage 176, 185
fg_getimage 105, 169, 172, 173, 185, 198
fg_getindex 73
fg_getkey 212, 231
fg_getlines 31, 36
fg_getmap 169-173, 181, 185, 198
fg_getmaxx 41, 42, 44, 86
fg_getmaxy 41, 42, 44, 86
fg_getmode 30, 36, 266
fg_getpage 127, 133
fg_getpixel 76, 77, 97
fg_getrgb 65, 66, 73
fg_getvpage 127, 133
fg_getworld 43, 44
fg_getxjoy 228-231
282 Fastgraph User's Guide
Fastgraph routines (continued)
fg_getxpos 79, 97
fg_getyjoy 228-231
fg_getypos 79, 97
fg_hush 240, 242, 244
fg_hushnext 240, 242, 243, 244
fg_imagesiz 172, 173, 185
fg_initems 129, 132, 133
fg_initjoy 132, 227, 228, 231
fg_initw 42, 44, 79, 108, 109
fg_initxms 129, 132, 133
fg_intjoy 229-231
fg_intkey 212, 213, 229, 231, 242, 248, 249
fg_locate 101-103, 107, 114, 120, 126, 173
fg_makepcx 164, 166, 185
fg_maprgb 68, 73
fg_measure 250, 251
fg_memavail 254, 256
fg_mousebut 218, 219, 231
fg_mousecur 220, 221, 231
fg_mouseini 132, 215, 216, 219, 231
fg_mouselim 216, 217, 231
fg_mousemov 216, 217, 231
fg_mousepos 218, 219, 231
fg_mouseptr 220, 225, 231
fg_mousespd 217, 231
fg_mousevis 216, 217, 219, 232
fg_move 79, 80, 82, 95, 98, 139, 140, 156, 170, 173, 181, 204
fg_moverel 79, 80, 98
fg_moverw 79, 98
fg_movew 79, 98
fg_music 237-239, 242, 244, 270
fg_musicb 242-244, 270
fg_numlock 213, 232
fg_paint 95, 98, 204
fg_paintw 95, 98
fg_palette 51-60, 62, 64, 65, 68, 69, 74
fg_palettes 69, 74
fg_pan 205-208
fg_panw 205, 206, 208
fg_pattern 158, 159, 163, 185
fg_playing 240, 243, 244
fg_point 76, 77, 98
fg_pointw 76, 98
fg_polygon 83, 98
fg_polygonw 83, 98
fg_quiet 234, 236, 237, 244
fg_rect 60, 65, 86, 87, 88, 98, 107, 120, 138, 141, 178, 188, 195
fg_rectw 86, 98, 113
fg_reset 29, 30, 36, 266
fg_resize 131-133, 207, 208
fg_restore 176-180, 185, 195, 201
fg_restorew 177, 185
fg_resume 243, 244
fg_revimage 149, 150, 166, 167, 169, 185
fg_revmask 166, 167, 185
Index 283
Fastgraph routines (continued)
fg_save 176-180, 185
fg_savew 177, 185
fg_scrlock 213, 232
fg_scroll 202-204, 208
fg_setangle 112, 114
fg_setattr 48-50, 74, 101, 102, 104, 114, 120, 121, 123, 125
fg_setcaps 214, 232
fg_setclip 76, 98, 148
fg_setclipw 76, 98
fg_setcolor 28, 49, 50, 51-60, 62, 64, 65, 74, 76, 79, 82, 92, 93,
101, 104, 108, 115, 120, 121, 123, 125, 138, 189, 190, 202,
203
fg_setdacs 66, 67, 74, 263
fg_setfunc 190, 255, 256
fg_sethpage 176, 177, 178, 185, 200, 202
fg_setlines 31, 36
fg_setmode 28, 30, 37, 50, 51, 57, 64, 69, 70, 76, 77, 79, 84, 86,
91, 92, 101, 102, 104, 109, 119, 120, 126, 132, 151, 159, 160,
161, 162, 210, 215, 227, 266
fg_setnum 214, 232
fg_setpage 119, 120, 133, 177
fg_setratio 109, 111, 115
fg_setrgb 53, 56, 60, 62, 64-66, 68, 73, 74, 163
fg_setsize 109, 115
fg_setsizew 109, 115
fg_setvpage 119, 128, 133, 196, 216
fg_setworld 42-44, 109
fg_sound 234-237, 239, 240, 245
fg_sounds 239, 240, 242, 245, 270
fg_stall 249-251
fg_suspend 243-245
fg_swchar 108-113, 115
fg_swlength 113, 115
fg_swtext 112, 113, 115
fg_tcmask 183, 185
fg_tcxfer 183, 186
fg_testmode 29, 30, 35, 37, 120, 122, 151, 177, 236
fg_text 28, 101, 102, 103, 104, 108, 115, 119
fg_transfer 179, 180, 181-183, 186, 194, 195, 196, 201
fg_version 5, 266
fg_voice 235-237, 239, 240, 241, 245
fg_voices 240-242, 245, 270
fg_waitfor 66, 188, 194, 196, 204, 206, 213, 217, 237, 248-251
fg_waitkey 29, 212, 232
fg_where 102, 103, 115, 126
fg_xalpha 44, 107, 115
fg_xconvert 44, 45, 107, 115
fg_xscreen 44, 45, 77
fg_xworld 44, 45, 79
fg_yalpha 44, 107, 115
fg_yconvert 44, 45, 107, 115
fg_yscreen 44, 45, 77
fg_yworld 44, 45, 79
Fastgraph/Light 2
Fastgraph/Light Video Driver 2, 16
284 Fastgraph User's Guide
FGDRIVER 16
/U option 16
FGTP unit 6
FGTPX unit 6
Filler bits 137
Foreground color 48, 49
FORTRAN see Microsoft FORTRAN
Game port 227
GrabRGB 158, 262, 263
/D option 263
output file format 263
Graphics cursor 79
Graphics modes 18, 21, 50
CGA 21, 51, 52
EGA 23, 56, 58, 59
extended VGA 24
Hercules 22, 54, 55
MCGA 24, 61, 64
native EGA 23
native VGA 24
PCjr 22, 53
Tandy 53
Tandy 1000 22
VGA 24, 61, 63, 64
Hardware characters 101
graphics modes 105
side effects 106
text modes 101
HERCFIX 263
Hidden page 119, 176
HIMEM.SYS 129
Hot spot 224
Image array 147
Image transfer routines 175
Images 136
clipped 148
regular 141
reversed 149
reversed clipped 149
INCLUDE environment variable 5, 6
Input devices 210
INSTALL program 4
/L option 4
Installation 4
Interrupts 270
INTRFACE.FOR 6
Joystick 227
button status 228, 229, 270
calibration 228
characteristics 228
horizontal position 228
initialize 227
keyboard emulation 229
special considerations 230
vertical position 228
Index 285
Keyboard 210
extended codes 210
standard codes 210
Keyboard buffer 212, 249
Keyboard state light 214
Lines
dashed 82
solid 79
Linking 5
Logical pages 129
creating 129
releasing 129
Masking map 166, 167
Memory models 3
large 3
medium 3
small 3
Memory update function 255
Mickeys 217
Microsoft C 3, 8
Microsoft FORTRAN 3, 9
Microsoft Mouse 215
Microsoft QuickBASIC 3, 10
Microsoft QuickC 3, 11
Mode X 26
Mouse 214
button status 218
CGA Considerations 226
cursor 216, 220
cursor mask 220, 222
cursor visibility 216
default cursor 222
hot spot 224
initialize 215
limits 216
position 216, 218
screen mask 220, 222
screen updates 216
speed 217
Mouse cursor 216, 220
graphics modes 222
hot spot 224
text modes 220
Mouse driver 215
Music 237
asynchronous 242
restarting 243
stopping 242
suspending 243
synchronous 237
Music commands 237
Music string 237, 242
Musical notes 237
Naming conventions 4
NumLock 212-214
Overscan 51
Packed pixel run map 154, 155
286 Fastgraph User's Guide
Palette 19
Palette number 53, 56, 58, 59, 62
Palette registers 53, 56, 58, 59, 61, 63, 69, 70
Palette value 53, 56, 58, 59
Palettes 53, 56, 58, 59, 61, 63
Panning 131, 207
PC Paintbrush 164
PCOPTION environment variable 5
PCX file
creating 164
displaying 164
video mode compatibility 165
PCX images 164
Periodic noise 236
Physical pages 118
Pixel bleeding 226
Pixel run 152
Pixel run file 156-158, 258
Pixel run map 152, 153, 158
Pixels 18
Points 76
Polygons 83
Power C 3, 13
Put_string 103
QuickBASIC see Microsoft QuickBASIC
QuickC see Microsoft QuickC
Real-time operations 248
Rectangles
dithered 88
solid 86
unfilled 87
Region fill 95
Register preservation 266
Resolution 18
Reverse video 104
RGB color mapping 68
Screen dissolving 200
Screen mask 220, 222
Screen origin 205
Screen space 40, 41
Scrolling 202
circular 202
end-off 202
Scrolling increment 202
Scrolling region 202
ScrollLock 212, 213
Shadow 104
Sliding tone 235
SNAPSHOT 158, 258, 259, 262, 263
error tone 258
image files 258
success tone 258
Software characters 108
alternate font 108, 109, 112
angle 112
aspect ratio 109
Software characters (continued)
font change operator (\) 109
primary font 108, 109
size 109
string length 113
subscript operator (\v) 110
superscript operator (\^) 110
underline operator (_) 110
Sound 234
asynchronous 239
disk accesses during 239
duration 234
frequency 234
PCjr 234
stopping 242
synchronous 234
Tandy 234
volume 234
Sound effects 234-236
Splitter cable 227Standard color set 50
Strlen 103
Stroke characters 108
Texas Instruments SN76496A sound chip 234
Text cursor 28, 101, 126
Text modes 18, 19, 48
43 lines 31
50 lines 31
color 48
monochrome 49
TI sound chip 234
Transparent color 183
Turbo C 3, 14
Turbo C++ 3, 14
Turbo Pascal 3, 15
Utilities 258
Vector characters 108
Video DAC registers 61, 62, 63-66, 69
Video modes 18
mode 00 20
mode 01 20
mode 02 20
mode 03 20
mode 04 21
mode 05 21
mode 06 22
mode 07 20
mode 09 22
mode 11 23
mode 12 23
mode 13 23
mode 14 24
mode 15 24
mode 16 24
mode 17 25
mode 18 25
mode 19 25
mode 20 25
Index 287
Video modes (continued)
mode 21 25
mode 22 26
mode 23 26
mode X 26
summary 18
Video page 118
Video page resizing 207
Video pages 19
active 119
hidden 119, 176
information 127
logical 129
physical 118
resizing 131
segment address 127
virtual 118, 122
visual 119
Video subsystem 19
Virtual colors 69, 70
Virtual pages 118
creating 122
releasing 122
special considerations 128
Visual effects 201
Visual page 119
Warbling 235
White noise 236
World space 40, 42
XMS 129